home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / blt1.000 / blt1 / blt-1.7-for-STk / blt-1.7 / src / bltDragDrop.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-12  |  72.5 KB  |  2,647 lines

  1. /*
  2.  * ------------------------------------------------------------------------
  3.  *  PURPOSE:  drag&drop widget registration facility
  4.  *
  5.  *  Allows widgets to be registered as drag&drop sources and targets
  6.  *  for handling "drag-and-drop" operations between Tcl/Tk applications.
  7.  *
  8.  *  USAGE:
  9.  *    blt_drag&drop source
  10.  *    blt_drag&drop source <pathName>
  11.  *    blt_drag&drop source <pathName> config ?options...?
  12.  *    blt_drag&drop source <pathName> handler ?<dataType> <cmd>...?
  13.  *
  14.  *    blt_drag&drop target
  15.  *    blt_drag&drop target <pathName> handler
  16.  *    blt_drag&drop target <pathName> handler ?<dataType> <cmd>...?
  17.  *    blt_drag&drop target <pathName> handle <dataType>
  18.  *
  19.  *    blt_drag&drop drag <pathName> <x> <y>
  20.  *    blt_drag&drop drop <pathName> <x> <y>
  21.  *
  22.  *    blt_drag&drop errors ?<proc>?
  23.  *    blt_drag&drop active
  24.  *    blt_drag&drop location ?<x> <y>?
  25.  *
  26.  * ------------------------------------------------------------------------
  27.  *  AUTHOR:  Michael J. McLennan       Phone: (215)770-2842
  28.  *           AT&T Bell Laboratories   E-mail: aluxpo!mmc@att.com
  29.  *
  30.  *     RCS:  bltDragDrop.c,v 1.10 1994/04/14 21:11:03 gah Exp
  31.  * ========================================================================
  32.  *                 Copyright (c) 1993-1994  AT&T Bell Laboratories
  33.  * ========================================================================
  34.  * Permission to use, copy, modify, and distribute this software and its
  35.  * documentation for any purpose and without fee is hereby granted,
  36.  * provided that the above copyright notice appear in all copies and that
  37.  * both that the copyright notice and warranty disclaimer appear in
  38.  * supporting documentation, and that the names of AT&T Bell Laboratories
  39.  * any of their entities not be used in advertising or publicity
  40.  * pertaining to distribution of the software without specific, written
  41.  * prior permission.
  42.  * 
  43.  * AT&T disclaims all warranties with regard to this software, including
  44.  * all implied warranties of merchantability and fitness.  In no event
  45.  * shall AT&T be liable for any special, indirect or consequential
  46.  * damages or any damages whatsoever resulting from loss of use, data or
  47.  * profits, whether in an action of contract, negligence or other
  48.  * tortuous action, arising out of or in connection with the use or
  49.  * performance of this software.
  50.  * ========================================================================
  51.  */
  52. #include "blt.h"
  53. #include <X11/Xatom.h>
  54.  
  55. #ifndef DRAGDROP_VERSION
  56. #define DRAGDROP_VERSION "2.1"
  57. #endif
  58.  
  59. #define DRAGDROP_COMMAND    "blt_drag&drop"   /* Command name */
  60. #define DRAGDROP_CLASS        "DragDrop"        /* CLASS NAME for token window */
  61. #define DRAGDROP_PROPINFO    "BltDragDropInfo" /* Property name */
  62. #define DRAGDROP_ERRORPROC    "tkerror"          /* Error Proc used to report 
  63.                                                * drag&drop background errors */
  64.  
  65. #ifndef _BLT_H
  66. /*
  67.  *  -------------  The following color definitions are taken directly
  68.  *  >> WARNING <<  from "default.h" in the Tk distribution, and should
  69.  *  -------------  be kept up to date with those conventions.
  70.  */
  71. #define BLACK       "Black"
  72. #define WHITE       "White"
  73. #define GRAY        "#b0b0b0"
  74.  
  75. #define BISQUE1     "#ffe4c4"
  76. #define BISQUE2     "#eed5b7"
  77. #define BISQUE3     "#cdb79e"
  78. #endif                            /* _BLT_H */
  79. #define DEF_BUTTON_ACTIVE_BG_COLOR  BISQUE2
  80. #define DEF_BUTTON_ACTIVE_BG_MONO   BLACK
  81. #define DEF_BUTTON_ACTIVE_FG_COLOR  BLACK
  82. #define DEF_BUTTON_ACTIVE_FG_MONO   WHITE
  83. #define DEF_BUTTON_BG_COLOR     BISQUE1
  84. #define DEF_BUTTON_BG_MONO      WHITE
  85. #define DEF_TOKEN_OUTLINE_COLOR     BLACK
  86. #define DEF_TOKEN_OUTLINE_MONO      BLACK
  87.  
  88. /*
  89.  *  DRAG&DROP ROOT WINDOW HIERARCHY (cached during "drag" operations)
  90.  */
  91. typedef struct DD_WinRep {
  92.     Window win;               /* X window for this record */
  93.     int initialized;          /* non-zero => rest of info is valid */
  94.     int x0, y0;               /* upper-left corner of window */
  95.     int x1, y1;               /* lower-right corner of window */
  96.     char *ddprop;             /* drag&drop property info */
  97.     char *ddinterp;           /* interp name within ddprop */
  98.     char *ddwin;              /* target window name within ddprop */
  99.     char *ddhandlers;         /* list of handlers within ddprop */
  100.     struct DD_WinRep* parent; /* window containing this as a child */
  101.     struct DD_WinRep* kids;   /* list of child windows */
  102.     struct DD_WinRep* next;   /* next sibling */
  103. } DD_WinRep;
  104.  
  105. /*
  106.  *  DRAG&DROP REGISTRATION DATA
  107.  */
  108. typedef struct {
  109.     Tcl_Interp *interp;       /* interpreter managing this drag&drop command */
  110.     Tk_Window root;           /* main window for application */
  111.     Tcl_HashTable srcList;    /* list of source widgets */
  112.     Tcl_HashTable trgList;    /* list of target widgets */
  113.     char *errorProc;          /* proc invoked for drag&drop errors */
  114.     int numactive;            /* number of active drag&drop operations */
  115.     int locx, locy;           /* last location point */
  116.     DD_WinRep *pool;          /* pool of available DD_WinRep records */
  117. } DD_RegList;
  118.  
  119. typedef struct {
  120.     DD_RegList *ddlist;       /* parent registration list */
  121.     Tk_Window tkwin;          /* registered window */
  122. } DD_RegEntry;
  123.  
  124. /*
  125.  *  DRAG&DROP SOURCE REGISTRATION RECORD
  126.  */
  127. typedef struct DD_SourceHndl {
  128.     char *dataType;               /* name of data type */
  129.     char *cmd;                    /* command used to send data */
  130.     struct DD_SourceHndl* next;   /* next handler in linked list */
  131. } DD_SourceHndl;
  132.  
  133. typedef struct {
  134.     DD_RegList *ddlist;           /* registration list containing this */
  135.  
  136.     Display *display;             /* drag&drop source window display */
  137.     Tk_Window tkwin;              /* drag&drop source window */
  138.     Atom ddAtom;                  /* X atom referring to "DragDropInfo" */
  139.     int button;                   /* button used to invoke drag for sources */
  140.  
  141.     Tk_Window tokenwin;           /* window representing drag item */
  142.     Tk_Anchor tokenAnchor;        /* position of token win relative to mouse */
  143.     Cursor tokenCursor;           /* cursor used when dragging token */
  144.     Tk_3DBorder tokenOutline;     /* outline around token window */
  145.     Tk_3DBorder tokenBorder;      /* border/background for token window */
  146.     int tokenBorderWidth;         /* border width in pixels */
  147.     XColor *rejectFg;             /* color used to draw rejection fg: (\) */
  148.     XColor *rejectBg;             /* color used to draw rejection bg: (\) */
  149.     Pixmap rejectSt;              /* stipple used to draw rejection: (\) */
  150.     GC rejectFgGC;                /* GC used to draw rejection fg: (\) */
  151.     GC rejectBgGC;                /* GC used to draw rejection bg: (\) */
  152.  
  153.     int pkgcmdInProg;             /* non-zero => executing pkgcmd */
  154.     char *pkgcmd;                 /* cmd executed on start of drag op */
  155.     char *pkgcmdResult;           /* result returned by recent pkgcmd */
  156.     char *sitecmd;                /* cmd executed to update token win */
  157.  
  158.     DD_WinRep *allwins;           /* window info (used during "drag") */
  159.     int selfTarget;               /* non-zero => source can drop onto itself */
  160.     int overTargetWin;            /* non-zero => over target window */
  161.     int tokenx, tokeny;           /* last position of token window */
  162.     Tk_TimerToken hidetoken;      /* token for routine to hide tokenwin */
  163.     Cursor normalCursor;          /* cursor restored after dragging */
  164.  
  165.     char *send;                   /* list of data handler names or "all" */
  166.     DD_SourceHndl *handlers;      /* list of data handlers */
  167. } DD_Source;
  168.  
  169. /*
  170.  *  STACK INFO
  171.  */
  172. typedef struct DD_Stack {
  173.     ClientData *values;     /* values on stack */
  174.     int len;                /* number of values on stack */
  175.     int max;                /* maximum size of stack */
  176.     ClientData space[5];    /* initial space for stack data */
  177. } DD_Stack;
  178.  
  179. /*
  180.  *  CONFIG PARAMETERS
  181.  */
  182. static Tk_ConfigSpec SourceConfigSpecs[] = {
  183.  
  184.     {TK_CONFIG_INT,
  185.         "-button", "buttonBinding", "ButtonBinding",
  186.         "3", Tk_Offset(DD_Source, button),
  187.         0},
  188.  
  189.     {TK_CONFIG_STRING,
  190.         "-packagecmd", "packageCommand", "Command",
  191.         NULL, Tk_Offset(DD_Source, pkgcmd),
  192.         TK_CONFIG_NULL_OK},
  193.  
  194.     {TK_CONFIG_COLOR,
  195.         "-rejectbg", "rejectBackground", "Background",
  196.         DEF_BUTTON_BG_COLOR, Tk_Offset(DD_Source, rejectBg),
  197.         TK_CONFIG_COLOR_ONLY},
  198.     {TK_CONFIG_COLOR,
  199.         "-rejectbg", "rejectBackground", "Background",
  200.         "white", Tk_Offset(DD_Source, rejectBg),
  201.         TK_CONFIG_MONO_ONLY},
  202.  
  203.     {TK_CONFIG_COLOR,
  204.         "-rejectfg", "rejectForeground", "Foreground",
  205.         "red", Tk_Offset(DD_Source, rejectFg),
  206.         TK_CONFIG_COLOR_ONLY},
  207.     {TK_CONFIG_COLOR,
  208.         "-rejectfg", "rejectForeground", "Foreground",
  209.         "black", Tk_Offset(DD_Source, rejectFg),
  210.         TK_CONFIG_MONO_ONLY},
  211.  
  212.     {TK_CONFIG_BITMAP,
  213.         "-rejectstipple", "rejectStipple", "Stipple",
  214.         (char*)NULL, Tk_Offset(DD_Source, rejectSt),
  215.         TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
  216.     {TK_CONFIG_BITMAP,
  217.         "-rejectstipple", "rejectStipple", "Stipple",
  218.         "gray50", Tk_Offset(DD_Source, rejectSt),
  219.         TK_CONFIG_MONO_ONLY},
  220.  
  221.     {TK_CONFIG_BOOLEAN,
  222.         "-selftarget", "selfTarget", "SelfTarget",
  223.         "no", Tk_Offset(DD_Source, selfTarget),
  224.         0},
  225.  
  226.     {TK_CONFIG_STRING,
  227.         "-send", "send", "Send",
  228.         "all", Tk_Offset(DD_Source, send),
  229.         TK_CONFIG_NULL_OK},
  230.  
  231.     {TK_CONFIG_STRING,
  232.         "-sitecmd", "siteCommand", "Command",
  233.         NULL, Tk_Offset(DD_Source, sitecmd),
  234.         TK_CONFIG_NULL_OK},
  235.  
  236.     {TK_CONFIG_ANCHOR,
  237.         "-tokenanchor", "tokenAnchor", "Anchor",
  238.         "center", Tk_Offset(DD_Source, tokenAnchor),
  239.         0},
  240.  
  241.     {TK_CONFIG_BORDER,
  242.         "-tokenbg", "tokenBackground", "Background",
  243.         DEF_BUTTON_BG_COLOR, Tk_Offset(DD_Source, tokenBorder),
  244.         TK_CONFIG_COLOR_ONLY},
  245.     {TK_CONFIG_BORDER,
  246.         "-tokenbg", "tokenBackground", "Background",
  247.         DEF_BUTTON_BG_MONO, Tk_Offset(DD_Source, tokenBorder),
  248.         TK_CONFIG_MONO_ONLY},
  249.  
  250.     {TK_CONFIG_BORDER,
  251.         "-tokenoutline", "tokenOutline", "Outline",
  252.         DEF_TOKEN_OUTLINE_COLOR, Tk_Offset(DD_Source, tokenOutline),
  253.         TK_CONFIG_COLOR_ONLY},
  254.     {TK_CONFIG_BORDER,
  255.         "-tokenoutline", "tokenOutline", "Outline",
  256.         DEF_TOKEN_OUTLINE_MONO, Tk_Offset(DD_Source, tokenOutline),
  257.         TK_CONFIG_MONO_ONLY},
  258.  
  259.     {TK_CONFIG_PIXELS,
  260.         "-tokenborderwidth", "tokenBorderWidth", "BorderWidth",
  261.         "3", Tk_Offset(DD_Source, tokenBorderWidth),
  262.         0},
  263.  
  264.     {TK_CONFIG_CURSOR,
  265.         "-tokencursor", "tokenCursor", "Cursor",
  266.         "center_ptr", Tk_Offset(DD_Source, tokenCursor),
  267.         TK_CONFIG_NULL_OK},
  268.  
  269.     {TK_CONFIG_END,
  270.         (char*)NULL, (char*)NULL, (char*)NULL,
  271.         (char*)NULL, 0,
  272.         0},
  273. };
  274.  
  275.  
  276. /*
  277.  *  DRAG&DROP TARGET REGISTRATION RECORD
  278.  */
  279. typedef struct DD_TargetHndl {
  280.     char *dataType;               /* Name of data type (malloc-ed) */
  281.     char *command;                /* command to handle data type (malloc-ed) */
  282.     struct DD_TargetHndl* next;   /* next handler in linked list */
  283. } DD_TargetHndl;
  284.  
  285. typedef struct {
  286.     DD_RegList *ddlist;           /* registration list containing this */
  287.  
  288.     Display *display;             /* drag&drop target window display */
  289.     Tk_Window tkwin;              /* drag&drop target window */
  290.     DD_TargetHndl *handlers;      /* list of data handlers */
  291. } DD_Target;
  292.  
  293. /*
  294.  * Each "drag&drop" widget window is tagged with a "DragDropInfo"
  295.  * property in XA_STRING format.  This property identifies the
  296.  * window as a "drag&drop" widget, and contains the following:
  297.  *
  298.  *     "<interp-name>]<drag&drop-path>]<handler-list>"
  299.  *
  300.  * The <drag&drop-path> is the window path name of the drag&drop
  301.  * widget, <interp-name> is the name of the interpreter controlling
  302.  * the widget (useful for the "send" command), and <handler-list>
  303.  * is the list of handler types recognized by the widget.
  304.  *
  305.  * When the user invokes the "drag" operation, a snapshot of the
  306.  * entire window hierarchy is made, and windows carrying a
  307.  * "DragDropInfo" property are identified.  As the token window is
  308.  * dragged around, * this snapshot can be queried to determine when
  309.  * the token is over a valid target window.  When the token is
  310.  * dropped over a valid site, the drop information is sent to the
  311.  * application via the usual "send" command.  If communication fails,
  312.  * the drag&drop facility automatically posts a rejection symbol on
  313.  * the token window.
  314.  */
  315.  
  316. /*
  317.  *  Maximum size property that can be read at one time:
  318.  */
  319. #define MAX_PROP_SIZE 1000
  320.  
  321. /*
  322.  *  FORWARD DECLARATIONS
  323.  */
  324. int Blt_DragDropInit _ANSI_ARGS_((Tcl_Interp* interp));
  325.  
  326. static void DragDrop_Delete _ANSI_ARGS_((ClientData clientData));
  327. static int DragDrop_Cmd _ANSI_ARGS_((ClientData clientData,
  328.     Tcl_Interp *interp, int argc, char **argv));
  329.  
  330. static DD_Source* GetSourceInfo _ANSI_ARGS_((DD_RegList *ddlist,
  331.     char *pathname, int *newEntry));
  332. static void DestroySourceInfo _ANSI_ARGS_((DD_RegList *ddlist,
  333.     char *pathName));
  334. static int ConfigSource _ANSI_ARGS_((Tcl_Interp *interp, DD_Source *dsPtr,
  335.     int argc, char **argv, int flags));
  336. static char* FindSourceHandler _ANSI_ARGS_((DD_Source* dsPtr, char *dtname));
  337. static void PutSourceHandler _ANSI_ARGS_((DD_Source* dsPtr, char *dtname,
  338.     char *cmd));
  339. static DD_SourceHndl* CreateSourceHandler _ANSI_ARGS_((char *dtname,
  340.     char *cmd));
  341. static void DestroySourceHandler _ANSI_ARGS_((DD_SourceHndl *dsHndl));
  342. static void UnregSource _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr));
  343.  
  344. static DD_Target* GetTargetInfo _ANSI_ARGS_((DD_RegList *ddlist,
  345.     char *pathname, int *newEntry));
  346. static void DestroyTargetInfo _ANSI_ARGS_((DD_RegList *ddlist,
  347.     char *pathName));
  348. static char* FindTargetHandler _ANSI_ARGS_((DD_Target* dtPtr, char *dtname));
  349. static void PutTargetHandler _ANSI_ARGS_((DD_Target* dtPtr, char *dtname,
  350.     char *cmd));
  351. static DD_TargetHndl* CreateTargetHandler _ANSI_ARGS_((char *dtname,
  352.     char *cmd));
  353. static void DestroyTargetHandler _ANSI_ARGS_((DD_TargetHndl *dtHndl));
  354. static void UnregTarget _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr));
  355.  
  356. static void DragDropSend _ANSI_ARGS_((DD_Source *dsPtr));
  357. static char* DragDropSendHndlr _ANSI_ARGS_((DD_Source *dsPtr,
  358.     char *interpName, char *ddName));
  359.  
  360. static DD_WinRep* GetWinRepInfo _ANSI_ARGS_((DD_Source *dsPtr,
  361.     DD_RegList *ddlist));
  362. static DD_WinRep* FindTargetWin _ANSI_ARGS_((DD_Source *dsPtr, int x, int y));
  363. static DD_WinRep* WinRepAlloc _ANSI_ARGS_((DD_RegList* ddlist));
  364. static void WinRepRelease _ANSI_ARGS_((DD_WinRep* wr, DD_RegList* ddlist));
  365. static void WinRepInit _ANSI_ARGS_((DD_WinRep* wr, DD_Source *dsPtr));
  366.  
  367. static void AddDDProp _ANSI_ARGS_((DD_Target *dtPtr));
  368. static void DDTokenEventProc _ANSI_ARGS_((ClientData, XEvent*));
  369. static void MoveDDToken _ANSI_ARGS_((DD_Source *dsPtr));
  370. static void UpdateDDToken _ANSI_ARGS_((ClientData clientData));
  371. static void HideDDToken _ANSI_ARGS_((ClientData clientData));
  372. static void RejectDDToken _ANSI_ARGS_((DD_Source* dsPtr));
  373.  
  374. static void StackInit _ANSI_ARGS_((DD_Stack *stack));
  375. static void StackDelete _ANSI_ARGS_((DD_Stack *stack));
  376. static void StackPush _ANSI_ARGS_((ClientData cdata, DD_Stack *stack));
  377. static ClientData StackPop _ANSI_ARGS_((DD_Stack *stack));
  378.  
  379.  
  380. /*
  381.  * ------------------------------------------------------------------------
  382.  *  Blt_DragDropInit()
  383.  *
  384.  *  Adds the drag&drop command to the given interpreter.  Should be
  385.  *  invoked to properly install the command whenever a new interpreter
  386.  *  is created.
  387.  * ------------------------------------------------------------------------
  388.  */
  389. int
  390. Blt_DragDropInit(interp)
  391.     Tcl_Interp *interp;  /* interpreter to be updated */
  392. {
  393.     DD_RegList *ddlist;
  394.     Tk_Window tkwin;
  395.     Tcl_CmdInfo cmdInfo;
  396.  
  397.     /*
  398.      *  See if drag&drop is already installed.
  399.      */
  400.     if (Tcl_GetCommandInfo(interp, DRAGDROP_COMMAND, &cmdInfo))
  401.     {
  402.         Tcl_ResetResult(interp);
  403.         Tcl_AppendResult(interp, "already installed: ", DRAGDROP_COMMAND,
  404.                          (char*)NULL);
  405.         return TCL_ERROR;
  406.     }
  407.  
  408.     /*
  409.      *  Make sure that this is a Tk application.
  410.      */
  411.     tkwin = Tk_MainWindow(interp);
  412.     if (tkwin == None)
  413.     {
  414.         Tcl_ResetResult(interp);
  415.         Tcl_AppendResult(interp, "requires Tk facilities: ", DRAGDROP_COMMAND,
  416.                          (char*)NULL);
  417.         return TCL_ERROR;
  418.     }
  419.  
  420.     /*
  421.      *  Install drag&drop facilities.
  422.      */
  423.     ddlist = (DD_RegList*)malloc(sizeof(DD_RegList));
  424.     ddlist->interp = interp;
  425.     ddlist->root = tkwin;
  426.     Tcl_InitHashTable(&ddlist->srcList,TCL_STRING_KEYS);
  427.     Tcl_InitHashTable(&ddlist->trgList,TCL_STRING_KEYS);
  428.     ddlist->errorProc = strdup(DRAGDROP_ERRORPROC);
  429.     ddlist->numactive = 0;
  430.     ddlist->locx = ddlist->locy = 0;
  431.     ddlist->pool = NULL;
  432.  
  433.     Tcl_CreateCommand(interp, DRAGDROP_COMMAND, DragDrop_Cmd,
  434.         (ClientData)ddlist, DragDrop_Delete);
  435.  
  436.     Tcl_SetVar2(interp, "blt_versions", DRAGDROP_COMMAND, DRAGDROP_VERSION,
  437.         TCL_GLOBAL_ONLY);
  438.  
  439.     return TCL_OK;
  440. }
  441.  
  442. /*
  443.  * ------------------------------------------------------------------------
  444.  *  DragDrop_Delete()
  445.  *
  446.  *  Invoked when the drag&drop command is removed from an interpreter
  447.  *  to free up allocated memory.
  448.  * ------------------------------------------------------------------------
  449.  */
  450. static void
  451. DragDrop_Delete(cdata)
  452.     ClientData cdata;    /* client data for drag&drop command */
  453. {
  454.     DD_RegList *ddlist = (DD_RegList*)cdata;
  455.     DD_WinRep *wrpool, *wrnext;
  456.  
  457.     Tcl_DeleteHashTable(&ddlist->srcList);
  458.     Tcl_DeleteHashTable(&ddlist->trgList);
  459.     if (ddlist->errorProc != NULL) {
  460.         free((char*)ddlist->errorProc);
  461.     }
  462.     for (wrpool=ddlist->pool; wrpool; wrpool=wrnext)
  463.     {
  464.         wrnext = wrpool->next;
  465.         free((char*)wrpool);
  466.     }
  467.     free((char*)ddlist);
  468. }
  469.  
  470. /*
  471.  * ------------------------------------------------------------------------
  472.  *  DragDrop_Cmd()
  473.  *
  474.  *  Invoked by TCL whenever the user issues a drag&drop command.
  475.  *  Handles the following syntax:
  476.  *
  477.  *    blt_drag&drop source
  478.  *    blt_drag&drop source <pathName>
  479.  *    blt_drag&drop source <pathName> config ?options...?
  480.  *    blt_drag&drop source <pathName> handler <dataType> <command> ?...?
  481.  *
  482.  *    blt_drag&drop target
  483.  *    blt_drag&drop target <pathName> handler
  484.  *    blt_drag&drop target <pathName> handler <dataType> <command> ?...?
  485.  *    blt_drag&drop target <pathName> handle <dataType>
  486.  *
  487.  *    blt_drag&drop drag <pathName> <x> <y>
  488.  *    blt_drag&drop drop <pathName> <x> <y>
  489.  *
  490.  *    blt_drag&drop errors ?<proc>?
  491.  *    blt_drag&drop active
  492.  *    blt_drag&drop location ?<x> <y>?
  493.  *
  494.  * ------------------------------------------------------------------------
  495.  */
  496. static int
  497. DragDrop_Cmd(clientData, interp, argc, argv)
  498.     ClientData clientData;   /* main window associated with interp */
  499.     Tcl_Interp *interp;      /* current interpreter */
  500.     int argc;                /* number of arguments */
  501.     char **argv;             /* argument strings */
  502. {
  503.     DD_RegList *ddlist = (DD_RegList*)clientData;
  504.     DD_RegEntry *ddentry;
  505.     register DD_Source *dsPtr;
  506.     register DD_Target *dtPtr;
  507.  
  508.     int status, length, x, y, newEntry;
  509.     char c;
  510.  
  511.     Tk_Window tokenwin;
  512.     XSetWindowAttributes atts;
  513.     char buffer[1024];
  514.  
  515.     if (argc < 2)
  516.     {
  517.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  518.             argv[0], " option ?args?\"",
  519.             (char*)NULL);
  520.         return TCL_ERROR;
  521.     }
  522.     c = argv[1][0];
  523.     length = strlen(argv[1]);
  524.  
  525.     /*
  526.      *  HANDLE:  blt_drag&drop source
  527.      *           blt_drag&drop source <pathName>
  528.      *           blt_drag&drop source <pathName> config ?options...?
  529.      *           blt_drag&drop source <pathName> handler <data> <scmd> ?...?
  530.      */
  531.     if ((c == 's') && strncmp(argv[1], "source", length) == 0)
  532.     {
  533.         /*
  534.          *  HANDLE:  blt_drag&drop source
  535.          */
  536.         if (argc == 2)
  537.         {
  538.             Tcl_HashSearch cursor;
  539.             Tcl_HashEntry *entryPtr;
  540.             char *name;
  541.             
  542.             for (entryPtr = Tcl_FirstHashEntry(&ddlist->srcList, &cursor);
  543.                  entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&cursor)) 
  544.             {
  545.                 name = Tcl_GetHashKey(&ddlist->srcList, entryPtr);
  546.                 Tcl_AppendElement(interp, name);
  547.             }
  548.             return TCL_OK;
  549.         }
  550.  
  551.         /*
  552.          *  Find or create source info...
  553.          */
  554.         dsPtr = GetSourceInfo(ddlist, argv[2], &newEntry);
  555.         dsPtr->tkwin = Tk_NameToWindow(interp, argv[2], ddlist->root);
  556.         if (!dsPtr->tkwin)
  557.         {
  558.             Tcl_AppendResult(interp, "window does not exist: ", argv[2],
  559.                 (char*)NULL);
  560.             DestroySourceInfo(ddlist,argv[2]);
  561.             return TCL_ERROR;
  562.         }
  563.         dsPtr->display = Tk_Display(dsPtr->tkwin);
  564.         dsPtr->ddAtom  = XInternAtom(dsPtr->display, DRAGDROP_PROPINFO, False);
  565.  
  566.         if (newEntry)
  567.             ConfigSource(interp, dsPtr, 0, (char**)NULL, 0);
  568.  
  569.         /*
  570.          *  HANDLE:  blt_drag&drop source <pathName> config ?options...?
  571.          */
  572.         if (argc > 3)
  573.         {
  574.             c = argv[3][0];
  575.             length = strlen(argv[3]);
  576.  
  577.             if ((c == 'c') && strncmp(argv[3], "config", length) == 0)
  578.             {
  579.                 if (argc == 4)
  580.                     status = Tk_ConfigureInfo(interp, dsPtr->tokenwin,
  581.                         SourceConfigSpecs, (char*)dsPtr, (char*)NULL, 0);
  582.  
  583.                 else if (argc == 5)
  584.                     status = Tk_ConfigureInfo(interp, dsPtr->tokenwin,
  585.                         SourceConfigSpecs, (char*)dsPtr, argv[4], 0);
  586.  
  587.                 else
  588.                     status = ConfigSource(interp, dsPtr, argc-4, argv+4,
  589.                         TK_CONFIG_ARGV_ONLY);
  590.             }
  591.  
  592.             /*
  593.              *  HANDLE:  blt_drag&drop source <pathName> handler \
  594.              *             ?<data> <scmd>...?
  595.              */
  596.             else if ((c == 'h') && strncmp(argv[3], "handler", length) == 0)
  597.             {
  598.                 if (argc == 4)
  599.                 {
  600.                     DD_SourceHndl *dsHndl = dsPtr->handlers;
  601.                     while (dsHndl)
  602.                     {
  603.                         Tcl_AppendElement(interp, dsHndl->dataType);
  604.                         dsHndl = dsHndl->next;
  605.                     }
  606.                     return TCL_OK;
  607.                 }
  608.  
  609.                 /*
  610.                  *  Process handler definitions
  611.                  */
  612.                 status = TCL_OK;
  613.                 for (x=4; (x < argc) && (status == TCL_OK); x+=2)
  614.                 {
  615.                     if (x+1 < argc)
  616.                     {
  617.                         char *p;
  618.                         for (p=argv[x]; *p != '\0'; p++)
  619.                             if (*p == ' ')
  620.                             {
  621.                                 Tcl_AppendResult(interp,
  622.                                     "bad source handler name \"",
  623.                                     argv[x], "\" (should not contain spaces)",
  624.                                     (char*)NULL);
  625.                                 return TCL_ERROR;
  626.                             }
  627.  
  628.                         PutSourceHandler(dsPtr, argv[x], argv[x+1]);
  629.                     }
  630.                     else
  631.                     {
  632.                         Tcl_AppendResult(interp,
  633.                             "missing command for source handler: should be \"",
  634.                             argv[x], " command\"");
  635.                         return TCL_ERROR;
  636.                     }
  637.                 }
  638.                 return TCL_OK;
  639.             }
  640.             else
  641.             {
  642.                 Tcl_AppendResult(interp, "bad option \"", argv[3],
  643.                     "\": must be config or handler",
  644.                     (char*)NULL);
  645.                 return TCL_ERROR;
  646.             }
  647.         }
  648.  
  649.         if (newEntry)
  650.         {
  651.             /*
  652.              *  Create the window for the drag&drop token...
  653.              */
  654.             sprintf(buffer, "dd-token%x", (int)dsPtr);
  655.             tokenwin = Tk_CreateWindow(dsPtr->ddlist->interp, dsPtr->tkwin,
  656.                 buffer, "");
  657.  
  658.             if (!tokenwin)
  659.             {
  660.                 Tcl_AppendResult(interp, "could not create token window",
  661.                     (char*)NULL);
  662.                 DestroySourceInfo(ddlist,argv[2]);
  663.                 return TCL_ERROR;
  664.             }
  665.             Tk_SetClass(tokenwin, DRAGDROP_CLASS);
  666.             Tk_CreateEventHandler(tokenwin, ExposureMask|StructureNotifyMask,
  667.                 DDTokenEventProc, (ClientData)dsPtr);
  668.  
  669.             atts.override_redirect = True;
  670.             atts.save_under = True;
  671.             Tk_ChangeWindowAttributes(tokenwin,
  672.                 CWOverrideRedirect|CWSaveUnder, &atts);
  673.  
  674.             Tk_SetInternalBorder(tokenwin, 2*dsPtr->tokenBorderWidth);
  675.             dsPtr->tokenwin = tokenwin;
  676.  
  677.             if (dsPtr->button > 0)
  678.             {
  679. #ifdef ORIGINAL_CODE
  680.                 sprintf(buffer,
  681.                     "bind %.100s <ButtonPress-%d> {%s drag %.100s %%X %%Y}; \
  682.                     bind %.100s <B%d-Motion> {%s drag %.100s %%X %%Y}; \
  683.                     bind %.100s <ButtonRelease-%d> {%s drop %.100s %%X %%Y}",
  684.                     argv[2], dsPtr->button, DRAGDROP_COMMAND, argv[2],
  685.                     argv[2], dsPtr->button, DRAGDROP_COMMAND, argv[2],
  686.                     argv[2], dsPtr->button, DRAGDROP_COMMAND, argv[2]);
  687. #else
  688.                 sprintf(buffer,
  689. "(begin \
  690.    (bind %.100s \"<ButtonPress-%d>\" '(%s 'drag %.100s |%%X| |%%Y|)) \
  691.    (bind %.100s \"<B%d-Motion>\" '(%s 'drag %.100s |%%X| |%%Y|)) \
  692.    (bind %.100s \"<ButtonRelease-%d>\" '(%s 'drop %.100s |%%X| |%%Y|)))",
  693.                     argv[2], dsPtr->button, DRAGDROP_COMMAND, argv[2],
  694.                     argv[2], dsPtr->button, DRAGDROP_COMMAND, argv[2],
  695.                     argv[2], dsPtr->button, DRAGDROP_COMMAND, argv[2]);
  696. #endif
  697.                 if (Tcl_Eval(interp, buffer) != TCL_OK)
  698.                 {
  699.                     Tk_DestroyWindow(tokenwin);
  700.                     DestroySourceInfo(ddlist,argv[2]);
  701.                     return TCL_ERROR;
  702.                 }
  703.             }
  704.  
  705.             /*
  706.              *  Arrange for the window to unregister itself when it
  707.              *  is destroyed.
  708.              */
  709.             ddentry = (DD_RegEntry*)malloc(sizeof(DD_RegEntry));
  710.             ddentry->ddlist = ddlist;
  711.             ddentry->tkwin = dsPtr->tkwin;
  712.             Tk_CreateEventHandler(dsPtr->tkwin, StructureNotifyMask,
  713.                 UnregSource, (ClientData)ddentry);
  714.         }
  715.     }
  716.  
  717.     /*
  718.      *  HANDLE:  blt_drag&drop target ?<pathName>? ?handling info...?
  719.      */
  720.     else if ((c == 't') && strncmp(argv[1], "target", length) == 0)
  721.     {
  722.         /*
  723.          *  HANDLE:  blt_drag&drop target
  724.          */
  725.         if (argc == 2)
  726.         {
  727.             Tcl_HashSearch pos;
  728.             Tcl_HashEntry *entry = Tcl_FirstHashEntry(&ddlist->trgList,&pos);
  729.             while (entry)
  730.             {
  731.                 Tcl_AppendElement(interp,
  732.                     Tcl_GetHashKey(&ddlist->trgList,entry));
  733.                 entry = Tcl_NextHashEntry(&pos);
  734.             }
  735.             return TCL_OK;
  736.         }
  737.  
  738.         dtPtr = GetTargetInfo(ddlist,argv[2],&newEntry);
  739.         dtPtr->tkwin = Tk_NameToWindow(interp, argv[2], ddlist->root);
  740.  
  741.         if (!dtPtr->tkwin)
  742.         {
  743.             Tcl_AppendResult(interp, "window does not exist: ", argv[2],
  744.                 (char*)NULL);
  745.             DestroyTargetInfo(ddlist,argv[2]);
  746.             return TCL_ERROR;
  747.         }
  748.         dtPtr->display = Tk_Display(dtPtr->tkwin);
  749.  
  750.         /*
  751.          *  If this is a new target, attach a property to identify
  752.          *  window as "drag&drop" target, and arrange for the window
  753.          *  to un-register itself when it is destroyed.
  754.          */
  755.         if (newEntry)
  756.         {
  757.             Tk_MakeWindowExist(dtPtr->tkwin);
  758.             AddDDProp(dtPtr);
  759.  
  760.             /*
  761.              *  Arrange for the window to unregister itself when it
  762.              *  is destroyed.
  763.              */
  764.             ddentry = (DD_RegEntry*)malloc(sizeof(DD_RegEntry));
  765.             ddentry->ddlist = ddlist;
  766.             ddentry->tkwin = dtPtr->tkwin;
  767.             Tk_CreateEventHandler(dtPtr->tkwin, StructureNotifyMask,
  768.                 UnregTarget, (ClientData)ddentry);
  769.         }
  770.  
  771.         /*
  772.          *  HANDLE:  blt_drag&drop target <pathName> handler
  773.          *           blt_drag&drop target <pathName> handler <data> <cmd> ?...?
  774.          */
  775.         if ((argc >= 4) && (strcmp(argv[3], "handler") == 0))
  776.         {
  777.             if (argc == 4)
  778.             {
  779.                 DD_TargetHndl *dtHndl = dtPtr->handlers;
  780.                 while (dtHndl)
  781.                 {
  782.                     Tcl_AppendElement(interp, dtHndl->dataType);
  783.                     dtHndl = dtHndl->next;
  784.                 }
  785.                 return TCL_OK;
  786.             }
  787.  
  788.             /*
  789.              *  Process handler definitions
  790.              */
  791.             status = TCL_OK;
  792.             for (x=4; (x < argc) && (status == TCL_OK); x+=2)
  793.             {
  794.                 if (x+1 < argc)
  795.                     PutTargetHandler(dtPtr, argv[x], argv[x+1]);
  796.                 else
  797.                 {
  798.                     Tcl_AppendResult(interp,
  799.                         "missing command for target handler: should be \"",
  800.                         argv[x], " command\"",
  801.                         (char*)NULL);
  802.                     return TCL_ERROR;
  803.                 }
  804.             }
  805.             return TCL_OK;
  806.         }
  807.  
  808.         /*
  809.          *  HANDLE:  blt_drag&drop target <pathName> handle <data>
  810.          */
  811.         else if ((argc == 5) && (strcmp(argv[3], "handle") == 0))
  812.         {
  813.             char *cmd;
  814.             if ((cmd=FindTargetHandler(dtPtr, argv[4])) != NULL)
  815.                 return Tcl_Eval(interp, cmd);
  816.  
  817.             Tcl_AppendResult(interp, "target cannot handle datatype: ",
  818.                 argv[4], (char*)NULL);
  819.             return TCL_ERROR;  /* no handler found */
  820.         }
  821.         else
  822.         {
  823.             Tcl_AppendResult(interp,"usage: ", argv[0], " target ", argv[2],
  824.                 " handler ?defns?\n   or: ", argv[0], " target ", argv[2],
  825.                 " handle <data>",
  826.                 (char*)NULL);
  827.             return TCL_ERROR;
  828.         }
  829.     }
  830.  
  831.     /*
  832.      *  HANDLE:  blt_drag&drop drag <path> <x> <y>
  833.      */
  834.     else if ((c == 'd') && strncmp(argv[1], "drag", length) == 0)
  835.     {
  836.         if (argc < 5)
  837.         {
  838.             Tcl_AppendResult(interp, "wrong # args: should be \"",
  839.                 argv[0], " drag pathname x y\"",
  840.                 (char*)NULL);
  841.             return TCL_ERROR;
  842.         }
  843.  
  844.         dsPtr = GetSourceInfo(ddlist,argv[2],&newEntry);
  845.         if (newEntry)
  846.         {
  847.             Tcl_AppendResult(interp, "not a drag&drop source: ", argv[2],
  848.                 (char*)NULL);
  849.             DestroySourceInfo(ddlist,argv[2]);
  850.             return TCL_ERROR;
  851.         }
  852.         if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK) ||
  853.             (Tcl_GetInt(interp, argv[4], &y) != TCL_OK))
  854.             return TCL_ERROR;
  855.  
  856.         ddlist->locx = x;   /* save drag&drop location */
  857.         ddlist->locy = y;
  858.         dsPtr->tokenx = x;
  859.         dsPtr->tokeny = y;
  860.  
  861.         /*
  862.          *  If HideDDToken() is pending, then do it now!
  863.          */
  864.         if (dsPtr->hidetoken)
  865.         {
  866.             Tk_DeleteTimerHandler(dsPtr->hidetoken);
  867.             HideDDToken((ClientData)dsPtr);
  868.         }
  869.  
  870.         /*
  871.          *  If pkgcmd is in progress, then ignore subsequent calls
  872.          *  until it completes.  Only perform drag if pkgcmd
  873.          *  completed successfully and token window is mapped.
  874.          */
  875.         if (!Tk_IsMapped(dsPtr->tokenwin) && !dsPtr->pkgcmdInProg)
  876.         {
  877.             /*
  878.              *  No list of send handlers?  Then source is disabled.
  879.              *  Abort drag quietly.
  880.              */
  881.             if (dsPtr->send == NULL)
  882.                 return TCL_OK;
  883.  
  884.             /*
  885.              *  No token command?  Then cannot build token.
  886.              *  Signal error.
  887.              */
  888.             if (!dsPtr->pkgcmd)
  889.             {
  890.                 Tcl_AppendResult(interp, "missing -packagecmd: ", argv[2],
  891.                     (char*)NULL);
  892.                 return TCL_ERROR;
  893.             }
  894.  
  895.             /*
  896.              *  Execute token command to initialize token window.
  897.              */
  898.             dsPtr->pkgcmdInProg = ~0;
  899.             status = Tcl_VarEval(dsPtr->ddlist->interp,
  900. #ifdef ORIGINAL_CODE
  901.                 dsPtr->pkgcmd, " ", Tk_PathName(dsPtr->tokenwin),
  902. #else
  903.                 "(", dsPtr->pkgcmd, " '", 
  904.                 Tk_PathName(dsPtr->tokenwin), ")",
  905. #endif
  906.                 (char*)NULL);
  907.             dsPtr->pkgcmdInProg = 0;
  908.  
  909.             /*
  910.              *  Null string from the package command?
  911.              *  Then quietly abort the drag&drop operation.
  912.              */
  913.             if (*interp->result == '\0')
  914.                 return TCL_OK;
  915.  
  916.             /*
  917.              *  Save result of token command for send command.
  918.              */
  919.             if (dsPtr->pkgcmdResult)
  920.                 free(dsPtr->pkgcmdResult);
  921.             dsPtr->pkgcmdResult = strdup(interp->result);
  922.  
  923.             /*
  924.              *  Token building failed?  If an error handler is defined,
  925.              *  then signal the error.  Otherwise, abort quietly.
  926.              */
  927.             if (status != TCL_OK)
  928.             {
  929.                 if (ddlist->errorProc && *ddlist->errorProc)
  930.                 {
  931.                     return Tcl_VarEval(ddlist->interp,
  932. #ifdef ORIGINAL_CODE
  933.                         ddlist->errorProc, " {", ddlist->interp->result, "}",
  934. #else
  935.                         "(", ddlist->errorProc, " ", (char *) STk_Stringify(ddlist->interp->result, 0), ")",
  936. #endif
  937.                         (char*)NULL);
  938.                 }
  939.                 else
  940.                     return TCL_OK;
  941.             }
  942.  
  943.             /*
  944.              *  Install token cursor...
  945.              */
  946.             if (dsPtr->tokenCursor != None)
  947.             {
  948.                 status = Tcl_VarEval(dsPtr->ddlist->interp,
  949. #ifdef ORIGINAL_CODE
  950.                     Tk_PathName(dsPtr->tkwin), " config -cursor",
  951. #else
  952.                         "(", Tk_PathName(dsPtr->tkwin), " 'config :cursor)",
  953. #endif
  954.                     (char*)NULL);
  955.  
  956.                 if (status == TCL_OK)
  957.                 {
  958.                     char *cname = interp->result;
  959.                     while (*cname != '\0')
  960.                         cname++;
  961.  
  962.                     while ((cname > interp->result) && (*(cname-1) != ' '))
  963.                         cname--;
  964.  
  965.                     if (dsPtr->normalCursor != None)
  966.                     {
  967.                         Tk_FreeCursor(dsPtr->display, dsPtr->normalCursor);
  968.                         dsPtr->normalCursor = None;
  969.                     }
  970.  
  971.                     if (strcmp(cname,"{}") != 0)
  972.                         dsPtr->normalCursor = Tk_GetCursor(interp,
  973.                             dsPtr->tkwin, Tk_GetUid(cname));
  974.                 }
  975.                 Tk_DefineCursor(dsPtr->tkwin, dsPtr->tokenCursor);
  976.             }
  977.  
  978.             /*
  979.              *  Get ready to drag token window...
  980.              *  1) Cache info for all windows on root
  981.              *  2) Map token window to begin drag operation
  982.              */
  983.             if (dsPtr->allwins)
  984.                 WinRepRelease(dsPtr->allwins, ddlist);
  985.             dsPtr->allwins = GetWinRepInfo(dsPtr, ddlist);
  986.  
  987.             ddlist->numactive++;   /* one more drag&drop window active */
  988.             Tk_MapWindow(dsPtr->tokenwin);
  989.             XRaiseWindow(Tk_Display(dsPtr->tokenwin),
  990.                 Tk_WindowId(dsPtr->tokenwin));
  991.         }
  992.  
  993.         /*
  994.          *  Arrange to update status of token window...
  995.          */
  996.         Tk_CancelIdleCall(UpdateDDToken, (ClientData)dsPtr);
  997.         Tk_DoWhenIdle(UpdateDDToken, (ClientData)dsPtr);
  998.  
  999.         /*
  1000.          *  Move the token window to the current drag point...
  1001.          */
  1002.         MoveDDToken(dsPtr);
  1003.     }
  1004.  
  1005.     /*
  1006.      *  HANDLE:  blt_drag&drop drop <path> <x> <y>
  1007.      */
  1008.     else if ((c == 'd') && strncmp(argv[1], "drop", length) == 0)
  1009.     {
  1010.         if (argc < 5)
  1011.         {
  1012.             Tcl_AppendResult(interp, "wrong # args: should be \"",
  1013.                 argv[0], " drop pathname x y\"",
  1014.                 (char*)NULL);
  1015.             return TCL_ERROR;
  1016.         }
  1017.  
  1018.         dsPtr = GetSourceInfo(ddlist,argv[2],&newEntry);
  1019.         if (newEntry)
  1020.         {
  1021.             Tcl_AppendResult(interp, "not a drag&drop source: ", argv[2],
  1022.                 (char*)NULL);
  1023.             DestroySourceInfo(ddlist,argv[2]);
  1024.             return TCL_ERROR;
  1025.         }
  1026.         if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK) ||
  1027.             (Tcl_GetInt(interp, argv[4], &y) != TCL_OK))
  1028.             return TCL_ERROR;
  1029.  
  1030.         ddlist->locx = x;  /* save drag&drop location */
  1031.         ddlist->locy = y;
  1032.         dsPtr->tokenx = x;
  1033.         dsPtr->tokeny = y;
  1034.  
  1035.         /*
  1036.          *  Put the cursor back to its usual state.
  1037.          */
  1038.         if (dsPtr->normalCursor == None)
  1039.             Tk_UndefineCursor(dsPtr->tkwin);
  1040.         else
  1041.             Tk_DefineCursor(dsPtr->tkwin, dsPtr->normalCursor);
  1042.  
  1043.         Tk_CancelIdleCall(UpdateDDToken, (ClientData)dsPtr);
  1044.  
  1045.         /*
  1046.          *  Make sure that token window was not dropped before it
  1047.          *  was either mapped or packed with info.
  1048.          */
  1049.         if (Tk_IsMapped(dsPtr->tokenwin) && !dsPtr->pkgcmdInProg)
  1050.         {
  1051.             UpdateDDToken((ClientData)dsPtr);
  1052.  
  1053.             if (dsPtr->send)
  1054.             {
  1055.                 if (dsPtr->overTargetWin)
  1056.                     DragDropSend(dsPtr);
  1057.                 else
  1058.                     HideDDToken((ClientData)dsPtr);
  1059.             }
  1060.             ddlist->numactive--;  /* one fewer active token window */
  1061.         }
  1062.     }
  1063.  
  1064.     /*
  1065.      *  HANDLE:  blt_drag&drop errors ?<proc>?
  1066.      */
  1067.     else if ((c == 'e') && strncmp(argv[1], "errors", length) == 0)
  1068.     {
  1069.         if (argc == 3)
  1070.         {
  1071.             if (ddlist->errorProc) {
  1072.                 free(ddlist->errorProc);
  1073.             }
  1074.             ddlist->errorProc = strdup(argv[2]);
  1075.         }
  1076.         else if (argc != 2)
  1077.         {
  1078.             Tcl_AppendResult(interp, "wrong # args: should be \"",
  1079.                 argv[0], " errors ?proc?\"",
  1080.                 (char*)NULL);
  1081.             return TCL_ERROR;
  1082.         }
  1083.         Tcl_SetResult(interp, ddlist->errorProc, TCL_VOLATILE);
  1084.         return TCL_OK;
  1085.     }
  1086.  
  1087.     /*
  1088.      *  HANDLE:  blt_drag&drop active
  1089.      */
  1090.     else if ((c == 'a') && strncmp(argv[1], "active", length) == 0)
  1091.     {
  1092.         if (argc != 2)
  1093.         {
  1094.             Tcl_AppendResult(interp, "wrong # args: should be \"",
  1095.                 argv[0], " active\"",
  1096.                 (char*)NULL);
  1097.             return TCL_ERROR;
  1098.         }
  1099.         Tcl_SetResult(interp, (ddlist->numactive > 0) ? "1" : "0", TCL_STATIC);
  1100.         return TCL_OK;
  1101.     }
  1102.  
  1103.     /*
  1104.      *  HANDLE:  blt_drag&drop location ?<x> <y>?
  1105.      */
  1106.     else if ((c == 'l') && strncmp(argv[1], "location", length) == 0)
  1107.     {
  1108.         if (argc == 2)
  1109.         {
  1110.             sprintf(buffer, "%d %d", ddlist->locx, ddlist->locy);
  1111.             Tcl_SetResult(interp, buffer, TCL_VOLATILE);
  1112.             return TCL_OK;
  1113.         }
  1114.         else if ((argc == 4) &&
  1115.             (Tcl_GetInt(interp, argv[2], &x) == TCL_OK) &&
  1116.             (Tcl_GetInt(interp, argv[3], &y) == TCL_OK))
  1117.         {
  1118.             ddlist->locx = x;
  1119.             ddlist->locy = y;
  1120.             sprintf(buffer, "%d %d", ddlist->locx, ddlist->locy);
  1121.             Tcl_SetResult(interp, buffer, TCL_VOLATILE);
  1122.             return TCL_OK;
  1123.         }
  1124.         else
  1125.         {
  1126.             Tcl_AppendResult(interp, "wrong # args: should be \"",
  1127.                 argv[0], " location ?x y?\"",
  1128.                 (char*)NULL);
  1129.             return TCL_ERROR;
  1130.         }
  1131.     }
  1132.  
  1133.     /*
  1134.      *  Report improper command arguments
  1135.      */
  1136.     else
  1137.     {
  1138.         Tcl_AppendResult(interp, "bad option \"", argv[1], "\": must be source, target, drag, drop, errors, active or location",
  1139.             (char*)NULL);
  1140.         return TCL_ERROR;
  1141.     }
  1142.     return TCL_OK;
  1143. }
  1144.  
  1145. /*
  1146.  * ------------------------------------------------------------------------
  1147.  *  GetSourceInfo()
  1148.  *
  1149.  *  Looks for a DD_Source record in the hash table for drag&drop source
  1150.  *  widgets.  Creates a new record if the widget name is not already
  1151.  *  registered.  Returns a pointer to the desired record.
  1152.  * ------------------------------------------------------------------------
  1153.  */
  1154. static DD_Source*
  1155. GetSourceInfo(ddlist,pathname,newEntry)
  1156.     DD_RegList* ddlist;  /* drag&drop records for all registered widgets */
  1157.     char* pathname;      /* widget pathname for desired record */
  1158.     int* newEntry;       /* returns non-zero => new record created */
  1159. {
  1160.     DD_Source *dsPtr;
  1161.     Tcl_HashEntry *ddEntry;
  1162.  
  1163.     ddEntry = Tcl_CreateHashEntry(&ddlist->srcList, pathname, newEntry);
  1164.     if (*newEntry)
  1165.     {
  1166.         /*
  1167.          *  Initialize a data structure for the widget...
  1168.          */
  1169.         dsPtr = (DD_Source*)malloc(sizeof(DD_Source));
  1170.         dsPtr->ddlist = ddlist;
  1171.         dsPtr->display = NULL;
  1172.         dsPtr->tkwin = NULL;
  1173.         dsPtr->ddAtom = None;
  1174.         dsPtr->button = 0;
  1175.  
  1176.         dsPtr->tokenwin = NULL;
  1177.         dsPtr->tokenAnchor = TK_ANCHOR_CENTER;
  1178.         dsPtr->tokenCursor = None;
  1179.         dsPtr->tokenOutline = NULL;
  1180.         dsPtr->tokenBorder = NULL;
  1181.         dsPtr->tokenBorderWidth = 0;
  1182.         dsPtr->rejectFg = NULL;
  1183.         dsPtr->rejectBg = NULL;
  1184.         dsPtr->rejectSt = None;
  1185.         dsPtr->rejectFgGC = None;
  1186.         dsPtr->rejectBgGC = None;
  1187.  
  1188.         dsPtr->pkgcmdInProg = 0;
  1189.         dsPtr->pkgcmd = NULL;
  1190.         dsPtr->pkgcmdResult = NULL;
  1191.         dsPtr->sitecmd = NULL;
  1192.  
  1193.         dsPtr->allwins = NULL;
  1194.         dsPtr->selfTarget = 0;
  1195.         dsPtr->overTargetWin = 0;
  1196.         dsPtr->tokenx = dsPtr->tokeny = 0;
  1197.         dsPtr->hidetoken = NULL;
  1198.         dsPtr->normalCursor = None;
  1199.  
  1200.         dsPtr->send = NULL;
  1201.         dsPtr->handlers = NULL;
  1202.  
  1203.         Tcl_SetHashValue(ddEntry, (ClientData)dsPtr);
  1204.     }
  1205.     return (DD_Source*)Tcl_GetHashValue(ddEntry);
  1206. }
  1207.  
  1208. /*
  1209.  * ------------------------------------------------------------------------
  1210.  *  DestroySourceInfo()
  1211.  *
  1212.  *  Looks for a DD_Source record in the hash table for drag&drop source
  1213.  *  widgets.  Destroys the record if found.
  1214.  * ------------------------------------------------------------------------
  1215.  */
  1216. static void
  1217. DestroySourceInfo(ddlist,pathname)
  1218.     DD_RegList* ddlist;     /* drag&drop records for all registered widgets */
  1219.     char* pathname;         /* widget pathname for desired record */
  1220. {
  1221.     DD_Source *dsPtr;
  1222.     DD_SourceHndl *dsHndl, *next;
  1223.     Tcl_HashEntry *ddEntry;
  1224.  
  1225.     ddEntry = Tcl_FindHashEntry(&ddlist->srcList, pathname);
  1226.     if (ddEntry == NULL) {
  1227.         return;
  1228.     }
  1229.     dsPtr = (DD_Source*)Tcl_GetHashValue(ddEntry);
  1230.     if (dsPtr)
  1231.     {
  1232.         Tk_CancelIdleCall(UpdateDDToken, (ClientData)dsPtr);
  1233.         if (dsPtr->hidetoken)           
  1234.             Tk_DeleteTimerHandler(dsPtr->hidetoken);
  1235.  
  1236.         Tk_FreeOptions(SourceConfigSpecs, (char *)dsPtr, dsPtr->display, 0);
  1237.  
  1238.         if (dsPtr->rejectFgGC != None) 
  1239.             Tk_FreeGC(dsPtr->display, dsPtr->rejectFgGC);
  1240.         if (dsPtr->rejectBgGC != None) 
  1241.             Tk_FreeGC(dsPtr->display, dsPtr->rejectBgGC);
  1242.         if (dsPtr->pkgcmdResult)       
  1243.             free(dsPtr->pkgcmdResult);
  1244.  
  1245.         if (dsPtr->allwins)               
  1246.             WinRepRelease(dsPtr->allwins, ddlist);
  1247.  
  1248.         if (dsPtr->normalCursor != None)
  1249.             Tk_FreeCursor(dsPtr->display, dsPtr->normalCursor);
  1250.  
  1251.         dsHndl = dsPtr->handlers;
  1252.         while (dsHndl)
  1253.         {
  1254.             next = dsHndl->next;
  1255.             DestroySourceHandler(dsHndl);
  1256.             dsHndl = next;
  1257.         }
  1258.         free((char*)dsPtr);
  1259.     }
  1260.     Tcl_DeleteHashEntry(ddEntry);
  1261. }
  1262.  
  1263. /*
  1264.  * ------------------------------------------------------------------------
  1265.  *  ConfigSource()
  1266.  *
  1267.  *  Called to process an (argc,argv) list to configure (or reconfigure)
  1268.  *  a drag&drop source widget.
  1269.  * ------------------------------------------------------------------------
  1270.  */
  1271. static int
  1272. ConfigSource(interp, dsPtr, argc, argv, flags)
  1273.     Tcl_Interp *interp;        /* current interpreter */
  1274.     register DD_Source *dsPtr; /* drag&drop source widget record */
  1275.     int argc;                  /* number of arguments */
  1276.     char **argv;               /* argument strings */
  1277.     int flags;                 /* flags controlling interpretation */
  1278. {
  1279.     unsigned long gcMask;
  1280.     XGCValues gcValues;
  1281.     GC newGC;
  1282.  
  1283.     /*
  1284.      *  Handle the bulk of the options...
  1285.      */
  1286.     if (Tk_ConfigureWidget(interp, dsPtr->tkwin, SourceConfigSpecs,
  1287.         argc, argv, (char*)dsPtr, flags) != TCL_OK)
  1288.         return TCL_ERROR;
  1289.  
  1290.     /*
  1291.      *  Check the button binding for valid range (0 or 1-5)
  1292.      */
  1293.     if (dsPtr->button < 0 || dsPtr->button > 5)
  1294.     {
  1295.         Tcl_SetResult(interp,
  1296.             "invalid button binding: should be 1-5 or 0 for no bindings",
  1297.             TCL_STATIC);
  1298.         return TCL_ERROR;
  1299.     }
  1300.     /*
  1301.      *  Set up the rejection foreground GC for the token window...
  1302.      */
  1303.     gcValues.foreground = dsPtr->rejectFg->pixel;
  1304.     gcValues.subwindow_mode = IncludeInferiors;
  1305.     gcValues.graphics_exposures = False;
  1306.     gcMask = GCForeground|GCSubwindowMode|GCGraphicsExposures;
  1307.  
  1308.     if (dsPtr->rejectSt != None)
  1309.     {
  1310.         gcValues.stipple = dsPtr->rejectSt;
  1311.         gcValues.fill_style = FillStippled;
  1312.         gcMask |= GCForeground|GCStipple|GCFillStyle;
  1313.     }
  1314.     newGC = Tk_GetGC(dsPtr->tkwin, gcMask, &gcValues);
  1315.  
  1316.     if (dsPtr->rejectFgGC != None)
  1317.         Tk_FreeGC(dsPtr->display, dsPtr->rejectFgGC);
  1318.     dsPtr->rejectFgGC = newGC;
  1319.  
  1320.     /*
  1321.      *  Set up the rejection background GC for the token window...
  1322.      */
  1323.     gcValues.foreground = dsPtr->rejectBg->pixel;
  1324.     gcValues.subwindow_mode = IncludeInferiors;
  1325.     gcValues.graphics_exposures = False;
  1326.     gcMask = GCForeground|GCSubwindowMode|GCGraphicsExposures;
  1327.  
  1328.     newGC = Tk_GetGC(dsPtr->tkwin, gcMask, &gcValues);
  1329.  
  1330.     if (dsPtr->rejectBgGC != None)
  1331.         Tk_FreeGC(dsPtr->display, dsPtr->rejectBgGC);
  1332.     dsPtr->rejectBgGC = newGC;
  1333.  
  1334.     /*
  1335.      *  Reset the border width in case it has changed...
  1336.      */
  1337.     if (dsPtr->tokenwin)
  1338.         Tk_SetInternalBorder(dsPtr->tokenwin, 2*dsPtr->tokenBorderWidth);
  1339.  
  1340.     return TCL_OK;
  1341. }
  1342.  
  1343. /*
  1344.  * ------------------------------------------------------------------------
  1345.  *  FindSourceHandler()
  1346.  *
  1347.  *  Looks for the requested data type of the list of handlers for the
  1348.  *  given drag&drop source.
  1349.  * ------------------------------------------------------------------------
  1350.  */
  1351. static char*
  1352. FindSourceHandler(dsPtr, dtname)
  1353.     register DD_Source *dsPtr; /* drag&drop source widget record */
  1354.     char *dtname;              /* name of requested data type */
  1355. {
  1356.     DD_SourceHndl *dsHndl;
  1357.  
  1358.     for (dsHndl=dsPtr->handlers; dsHndl; dsHndl=dsHndl->next)
  1359.         if (strcmp(dsHndl->dataType,dtname) == 0)
  1360.             return dsHndl->cmd;
  1361.  
  1362.     return NULL;
  1363. }
  1364.  
  1365. /*
  1366.  * ------------------------------------------------------------------------
  1367.  *  PutSourceHandler()
  1368.  *
  1369.  *  Looks for the requested data type of the list of handlers for the
  1370.  *  given drag&drop source.  If found, then its associated commands are
  1371.  *  changed to the given commands.  If not found, then a new handler
  1372.  *  is created.
  1373.  * ------------------------------------------------------------------------
  1374.  */
  1375. static void
  1376. PutSourceHandler(dsPtr, dtname, cmd)
  1377.     register DD_Source *dsPtr; /* drag&drop target widget record */
  1378.     char *dtname;              /* name of data type */
  1379.     char *cmd;                 /* command used to send data */
  1380. {
  1381.     DD_SourceHndl *tail = NULL;
  1382.     DD_SourceHndl *dsHndl;
  1383.  
  1384.     for (dsHndl=dsPtr->handlers; dsHndl; tail=dsHndl,dsHndl=dsHndl->next)
  1385.         if (strcmp(dsHndl->dataType,dtname) == 0)
  1386.         {
  1387.             if (*cmd == '\0')
  1388.             {
  1389.                 if (tail)
  1390.                     tail->next = dsHndl->next;
  1391.                 else
  1392.                     dsPtr->handlers = dsHndl->next;
  1393.  
  1394.                 DestroySourceHandler(dsHndl);
  1395.                 return;
  1396.             }
  1397.             else
  1398.             {
  1399.                 if (dsHndl->cmd != NULL) {
  1400.                     free(dsHndl->cmd);
  1401.                 }
  1402.                 dsHndl->cmd = strdup(cmd);
  1403.                 return;
  1404.             }
  1405.         }
  1406.  
  1407.     if (tail)
  1408.         tail->next = CreateSourceHandler(dtname,cmd);
  1409.     else
  1410.         dsPtr->handlers = CreateSourceHandler(dtname,cmd);
  1411. }
  1412.  
  1413. /*
  1414.  * ------------------------------------------------------------------------
  1415.  *  CreateSourceHandler()
  1416.  *
  1417.  *  Creates a new source handler record and returns a pointer to it.
  1418.  * ------------------------------------------------------------------------
  1419.  */
  1420. static DD_SourceHndl*
  1421. CreateSourceHandler(dtname, cmd)
  1422.     char *dtname;              /* name of data type */
  1423.     char *cmd;                 /* command used to send data */
  1424. {
  1425.     DD_SourceHndl *retn;
  1426.     retn = (DD_SourceHndl*)malloc(sizeof(DD_SourceHndl));
  1427.  
  1428.     retn->dataType = strdup(dtname);
  1429.     retn->cmd = strdup(cmd);
  1430.     retn->next = NULL;
  1431.     return retn;
  1432. }
  1433.  
  1434. /*
  1435.  * ------------------------------------------------------------------------
  1436.  *  DestroySourceHandler()
  1437.  *
  1438.  *  Destroys a source handler record.
  1439.  * ------------------------------------------------------------------------
  1440.  */
  1441. static void
  1442. DestroySourceHandler(dsHndl)
  1443.     DD_SourceHndl *dsHndl;
  1444. {
  1445.     if (dsHndl->dataType != NULL) {
  1446.         free(dsHndl->dataType);
  1447.     }
  1448.     if (dsHndl->cmd != NULL) {
  1449.         free(dsHndl->cmd);
  1450.     }
  1451.     free((char*)dsHndl);
  1452. }
  1453.  
  1454. /*
  1455.  * ------------------------------------------------------------------------
  1456.  *  UnregSource()
  1457.  *
  1458.  *  Invoked by Tk_HandleEvent whenever a DestroyNotify event is received
  1459.  *  on a registered drag&drop source widget.
  1460.  * ------------------------------------------------------------------------
  1461.  */
  1462. static void
  1463. UnregSource(cdata, eventPtr)
  1464.     ClientData cdata;   /* drag&drop registration list */
  1465.     XEvent *eventPtr;   /* event description */
  1466. {
  1467.     DD_RegEntry *ddentry = (DD_RegEntry*)cdata;
  1468.     DD_RegList *ddlist = ddentry->ddlist;
  1469.     char *ddname = Tk_PathName(ddentry->tkwin);
  1470.  
  1471.     if (eventPtr->type == DestroyNotify)
  1472.     {
  1473.         DestroySourceInfo(ddlist,ddname);
  1474.         free((char*)ddentry);
  1475.     }
  1476. }
  1477.  
  1478. /*
  1479.  * ------------------------------------------------------------------------
  1480.  *  GetTargetInfo()
  1481.  *
  1482.  *  Looks for a DD_Target record in the hash table for drag&drop target
  1483.  *  widgets.  Creates a new record if the widget name is not already
  1484.  *  registered.  Returns a pointer to the desired record.
  1485.  * ------------------------------------------------------------------------
  1486.  */
  1487. static DD_Target*
  1488. GetTargetInfo(ddlist,pathname,newEntry)
  1489.     DD_RegList* ddlist;  /* drag&drop records for all registered widgets */
  1490.     char* pathname;      /* widget pathname for desired record */
  1491.     int* newEntry;       /* returns non-zero => new record created */
  1492. {
  1493.     DD_Target *dtPtr;
  1494.     Tcl_HashEntry *ddEntry;
  1495.  
  1496.     ddEntry = Tcl_CreateHashEntry(&ddlist->trgList, pathname, newEntry);
  1497.     if (*newEntry)
  1498.     {
  1499.         /*
  1500.          *  Initialize a data structure for the widget...
  1501.          */
  1502.         dtPtr = (DD_Target*)malloc(sizeof(DD_Target));
  1503.         dtPtr->ddlist = ddlist;
  1504.         dtPtr->display = NULL;
  1505.         dtPtr->tkwin = NULL;
  1506.         dtPtr->handlers = NULL;
  1507.  
  1508.         Tcl_SetHashValue(ddEntry, (ClientData)dtPtr);
  1509.     }
  1510.     return (DD_Target*)Tcl_GetHashValue(ddEntry);
  1511. }
  1512.  
  1513. /*
  1514.  * ------------------------------------------------------------------------
  1515.  *  DestroyTargetInfo()
  1516.  *
  1517.  *  Looks for a DD_Target record in the hash table for drag&drop target
  1518.  *  widgets.  Destroys the record if found.
  1519.  * ------------------------------------------------------------------------
  1520.  */
  1521. static void
  1522. DestroyTargetInfo(ddlist,pathname)
  1523.     DD_RegList* ddlist;  /* drag&drop records for all registered widgets */
  1524.     char* pathname;      /* widget pathname for desired record */
  1525. {
  1526.     DD_Target *dtPtr;
  1527.     DD_TargetHndl *dtHndl, *next;
  1528.     Tcl_HashEntry *ddEntry;
  1529.  
  1530.     ddEntry = Tcl_FindHashEntry(&ddlist->trgList, pathname);
  1531.     dtPtr = (ddEntry) ? (DD_Target*)Tcl_GetHashValue(ddEntry) : NULL;
  1532.  
  1533.     if (dtPtr)
  1534.     {
  1535.         dtHndl = dtPtr->handlers;
  1536.         while (dtHndl)
  1537.         {
  1538.             next = dtHndl->next;
  1539.             DestroyTargetHandler(dtHndl);
  1540.             dtHndl = next;
  1541.         }
  1542.         free((char*)dtPtr);
  1543.     }
  1544.     if (ddEntry) Tcl_DeleteHashEntry(ddEntry);
  1545. }
  1546.  
  1547. /*
  1548.  * ------------------------------------------------------------------------
  1549.  *  FindTargetHandler()
  1550.  *
  1551.  *  Looks for the requested data type of the list of handlers for the
  1552.  *  given drag&drop target.
  1553.  * ------------------------------------------------------------------------
  1554.  */
  1555. static char*
  1556. FindTargetHandler(dtPtr, dtname)
  1557.     register DD_Target *dtPtr; /* drag&drop target widget record */
  1558.     char *dtname;              /* name of requested data type */
  1559. {
  1560.     DD_TargetHndl *dtHndl;
  1561.  
  1562.     for (dtHndl=dtPtr->handlers; dtHndl; dtHndl=dtHndl->next)
  1563.         if (strcmp(dtHndl->dataType,dtname) == 0)
  1564.             return dtHndl->command;
  1565.  
  1566.     return NULL;
  1567. }
  1568.  
  1569. /*
  1570.  * ------------------------------------------------------------------------
  1571.  *  PutTargetHandler()
  1572.  *
  1573.  *  Looks for the requested data type of the list of handlers for the
  1574.  *  given drag&drop target.  If found, then its associated command is
  1575.  *  changed to the given command.  If not found, then a new handler
  1576.  *  is created.
  1577.  * ------------------------------------------------------------------------
  1578.  */
  1579. static void
  1580. PutTargetHandler(dtPtr, dtname, cmd)
  1581.     register DD_Target *dtPtr; /* drag&drop target widget record */
  1582.     char *dtname;              /* name of data type */
  1583.     char *cmd;                 /* command string for data type */
  1584. {
  1585.     DD_TargetHndl *tail = NULL;
  1586.     DD_TargetHndl *dtHndl;
  1587.  
  1588.     for (dtHndl=dtPtr->handlers; dtHndl; tail=dtHndl,dtHndl=dtHndl->next)
  1589.         if (strcmp(dtHndl->dataType,dtname) == 0)
  1590.         {
  1591.             if (*cmd == '\0')
  1592.             {
  1593.                 if (tail)
  1594.                     tail->next = dtHndl->next;
  1595.                 else
  1596.                     dtPtr->handlers = dtHndl->next;
  1597.  
  1598.                 DestroyTargetHandler(dtHndl);
  1599.                 return;
  1600.             }
  1601.             else
  1602.             {
  1603.                 if (dtHndl->command != NULL) {
  1604.                     free(dtHndl->command);
  1605.                 }
  1606.                 dtHndl->command = strdup(cmd);
  1607.                 return;
  1608.             }
  1609.         }
  1610.  
  1611.     if (tail)
  1612.         tail->next = CreateTargetHandler(dtname,cmd);
  1613.     else
  1614.         dtPtr->handlers = CreateTargetHandler(dtname,cmd);
  1615.  
  1616.     /*
  1617.      *  Update handler list stored in target window property.
  1618.      */
  1619.     AddDDProp(dtPtr);
  1620. }
  1621.  
  1622. /*
  1623.  * ------------------------------------------------------------------------
  1624.  *  CreateTargetHandler()
  1625.  *
  1626.  *  Creates a new target handler record and returns a pointer to it.
  1627.  * ------------------------------------------------------------------------
  1628.  */
  1629. static DD_TargetHndl*
  1630. CreateTargetHandler(dtname, cmd)
  1631.     char *dtname;              /* name of data type */
  1632.     char *cmd;                 /* command string for data type */
  1633. {
  1634.     DD_TargetHndl *retn;
  1635.     retn = (DD_TargetHndl*)malloc(sizeof(DD_TargetHndl));
  1636.  
  1637.     retn->dataType = strdup(dtname);
  1638.     retn->command = strdup(cmd);
  1639.  
  1640.     retn->next = NULL;
  1641.     return retn;
  1642. }
  1643.  
  1644. /*
  1645.  * ------------------------------------------------------------------------
  1646.  *  DestroyTargetHandler()
  1647.  *
  1648.  *  Destroys a target handler record.
  1649.  * ------------------------------------------------------------------------
  1650.  */
  1651. static void
  1652. DestroyTargetHandler(dtHndl)
  1653.     DD_TargetHndl *dtHndl;
  1654. {
  1655.     if (dtHndl->dataType != NULL) {
  1656.         free(dtHndl->dataType);
  1657.     }
  1658.     if (dtHndl->command != NULL) {
  1659.         free(dtHndl->command);
  1660.     }
  1661.     free((char*)dtHndl);
  1662. }
  1663.  
  1664. /*
  1665.  * ------------------------------------------------------------------------
  1666.  *  UnregTarget()
  1667.  *
  1668.  *  Invoked by Tk_HandleEvent whenever a DestroyNotify event is received
  1669.  *  on a registered drag&drop target widget.
  1670.  * ------------------------------------------------------------------------
  1671.  */
  1672. static void
  1673. UnregTarget(cdata, eventPtr)
  1674.     ClientData cdata;   /* drag&drop registration list */
  1675.     XEvent *eventPtr;   /* event description */
  1676. {
  1677.     DD_RegEntry *ddentry = (DD_RegEntry*)cdata;
  1678.     DD_RegList *ddlist = ddentry->ddlist;
  1679.     char *ddname = Tk_PathName(ddentry->tkwin);
  1680.  
  1681.     if (eventPtr->type == DestroyNotify)
  1682.     {
  1683.         DestroyTargetInfo(ddlist,ddname);
  1684.         free((char*)ddentry);
  1685.     }
  1686. }
  1687.  
  1688. /*
  1689.  * ------------------------------------------------------------------------
  1690.  *  DragDropSend()
  1691.  *
  1692.  *  Invoked after a drop operation to send data to the drop application.
  1693.  * ------------------------------------------------------------------------
  1694.  */
  1695. static void
  1696. DragDropSend(dsPtr)
  1697.     register DD_Source *dsPtr;  /* drag&drop source record */
  1698. {
  1699.     DD_RegList *ddlist = dsPtr->ddlist;
  1700.  
  1701.     int status;
  1702.     char *sendcmd;
  1703.     DD_WinRep *target;
  1704.  
  1705.     /*
  1706.      *  See if current position is over drop point...
  1707.      */
  1708.     target = FindTargetWin(dsPtr, dsPtr->tokenx,dsPtr->tokeny);
  1709.  
  1710.     if (target)
  1711.     {
  1712.         char buffer[256];
  1713.  
  1714.         sprintf(buffer, "%d %d", dsPtr->tokenx, dsPtr->tokeny);
  1715.         status = Tcl_VarEval(ddlist->interp,
  1716. #ifdef ORIGINAL_CODE
  1717.             "send {",target->ddinterp,"} ", DRAGDROP_COMMAND," location ",buffer,
  1718. #else
  1719.             "(send ",target->ddinterp," '(", DRAGDROP_COMMAND," 'location ",buffer,"))", 
  1720. #endif
  1721.             (char*)NULL);
  1722.  
  1723.         if (status == TCL_OK)
  1724.         {
  1725.             sendcmd = DragDropSendHndlr(dsPtr, target->ddinterp, target->ddwin);
  1726.             if (sendcmd)
  1727.             {
  1728.                 status = Tcl_VarEval(ddlist->interp,
  1729. #ifdef ORIGINAL_CODE
  1730.                     sendcmd, " {",target->ddinterp,"} {",target->ddwin,
  1731.                     "} {",dsPtr->pkgcmdResult,"}",
  1732. #else
  1733.                     "(", sendcmd, " ",target->ddinterp," '",target->ddwin,
  1734.                     " '",dsPtr->pkgcmdResult,")",
  1735. #endif                             
  1736.                     (char*)NULL);
  1737.             }
  1738.             else
  1739.             {
  1740.                 Tcl_AppendResult(ddlist->interp, "target \"",
  1741.                     target->ddwin,
  1742.                     "\" does not recognize handlers for source \"",
  1743.                     Tk_PathName(dsPtr->tkwin), "\"",
  1744.                     (char*)NULL);
  1745.                 status = TCL_ERROR;
  1746.             }
  1747.         }
  1748.  
  1749.         /*
  1750.          *  Give success/failure feedback to user.
  1751.          *  If an error occurred and an error proc is defined,
  1752.          *  then use it to handle the error.
  1753.          */
  1754.         if (status == TCL_OK)
  1755.             HideDDToken((ClientData)dsPtr);
  1756.         else
  1757.         {
  1758.             RejectDDToken(dsPtr);
  1759.  
  1760.             if (ddlist->errorProc && *ddlist->errorProc)
  1761.                 (void) Tcl_VarEval(ddlist->interp,
  1762. #ifdef ORIGINAL_CODE
  1763.                     ddlist->errorProc, " {", ddlist->interp->result, "}",
  1764. #else
  1765.                     "(", ddlist->errorProc, " ", 
  1766.                      (char *) STk_Stringify(ddlist->interp->result, 0), ")",
  1767. #endif
  1768.                     (char*)NULL);
  1769.         }
  1770.     }
  1771. }
  1772.  
  1773. /*
  1774.  * ------------------------------------------------------------------------
  1775.  *  DragDropSendHndlr()
  1776.  *
  1777.  *  Queries the drag&drop target under the specified interpreter for a
  1778.  *  handler that is compatible with one of the handlers defined for the
  1779.  *  source.  Returns a pointer to the appropriate send command, or
  1780.  *  NULL if none is found.
  1781.  * ------------------------------------------------------------------------
  1782.  */
  1783. static char*
  1784. DragDropSendHndlr(dsPtr,interpName,ddName)
  1785.     register DD_Source *dsPtr;  /* drag&drop source record */
  1786.     char *interpName;           /* interpreter containing drag&drop target */
  1787.     char *ddName;               /* drag&drop target pathname */
  1788. {
  1789.     char *retn = NULL;  /* no handler found yet */
  1790.     Tcl_Interp *interp = dsPtr->ddlist->interp;
  1791.  
  1792.     int hndlc, hi, ei;
  1793.     char **hndlv, *hlist;
  1794.     DD_SourceHndl *dsHndl;
  1795.     char buffer[1024];
  1796.  
  1797.     /*
  1798.      *  Query the drag&drop target for its list of known handlers.
  1799.      */
  1800.     Tcl_ResetResult(interp); /* for Tcl_AppendResult() below */
  1801.     if (Tcl_VarEval(interp,
  1802. #ifdef ORIGINAL_CODE
  1803.         "send {",interpName,"} ", DRAGDROP_COMMAND," target {",ddName,"} handler",
  1804. #else
  1805.         "(send ",interpName," '(", DRAGDROP_COMMAND," 'target '",ddName," 'handler))",
  1806. #endif
  1807.         (char*)NULL) != TCL_OK)
  1808.         return NULL;
  1809. #ifdef ORIGINAL_CODE
  1810.     hlist = strdup(interp->result);
  1811. #else
  1812.     /* DIRTY HACKS..... But I don't know how to make it work better */
  1813.     /* Result of send is always a string in STk. So there are '"' to delete */
  1814.     /* Furthermore, The STk Tcl_SplitList doesn't handle very well lists */
  1815.     if (*(interp->result)) {
  1816.       int x = ((interp->result)[1]) == '('? 2: 1;
  1817.  
  1818.       hlist = strdup(interp->result + x);
  1819.       hlist[strlen(hlist)-x] = '\0';
  1820.     }
  1821.     else 
  1822.       hlist = strdup(interp->result);
  1823. #endif
  1824.     if (Tcl_SplitList(interp, hlist, &hndlc, &hndlv) == TCL_OK)
  1825.     {
  1826.         /*
  1827.          *  If the list of send handlers is specified as "all", then
  1828.          *  search through the handlers in order.
  1829.          */
  1830.         if (strcmp(dsPtr->send,"all") == 0)
  1831.             for (dsHndl=dsPtr->handlers; dsHndl && !retn; dsHndl=dsHndl->next)
  1832.             {
  1833.                 for (hi=0; (hi < hndlc) && !retn; hi++)
  1834.                     if (strcmp(dsHndl->dataType, hndlv[hi]) == 0)
  1835.                         retn = dsHndl->cmd;
  1836.             }
  1837.  
  1838.         /*
  1839.          *  Otherwise, search through the specified send handlers.
  1840.          */
  1841.         else
  1842.         {
  1843.             int elemc;
  1844.             char **elemv;
  1845.             if (Tcl_SplitList(interp,dsPtr->send,&elemc,&elemv)==TCL_OK)
  1846.             {
  1847.                 for (ei=0; (ei < elemc) && !retn; ei++)
  1848.                     for (hi=0; (hi < hndlc) && !retn; hi++)
  1849.                         if (strcmp(elemv[ei], hndlv[hi]) == 0)
  1850.                         {
  1851.                             retn = FindSourceHandler(dsPtr, elemv[ei]);
  1852.                             if (!retn)
  1853.                             {
  1854.                                 sprintf(buffer, "unknown handler \"%.50s\" requested for drag&drop source \"%.200s\"", elemv[ei], Tk_PathName(dsPtr->tkwin));
  1855.                                 Tcl_ResetResult(interp);
  1856.                                 Tcl_AddErrorInfo(interp, buffer);
  1857.                                 Tk_BackgroundError(interp);
  1858.                             }
  1859.                         }
  1860.  
  1861.                 free((char*)elemv);
  1862.             }
  1863.             else
  1864.             {
  1865.                 sprintf(buffer, "drag&drop source has invalid -send: %.200s",
  1866.                     dsPtr->send);
  1867.                 Tcl_ResetResult(interp);
  1868.                 Tcl_AddErrorInfo(interp, buffer);
  1869.                 Tk_BackgroundError(interp);
  1870.             }
  1871.         }
  1872.         free((char*)hndlv);
  1873.     }
  1874.     free(hlist);
  1875.  
  1876.     return retn;
  1877. }
  1878.  
  1879.  
  1880. /*
  1881.  * ------------------------------------------------------------------------
  1882.  *  GetWinRepInfo()
  1883.  *
  1884.  *  Invoked at the start of a "drag" operation to capture the positions
  1885.  *  of all windows on the current root.  Queries the entire window
  1886.  *  hierarchy and determines the placement of each window.  Queries
  1887.  *  "DragDropInfo" property info where appropriate.  This information
  1888.  *  is used during the drag operation to determine when the drag&drop
  1889.  *  token is over a valid drag&drop target.
  1890.  *
  1891.  *  Returns the record for the root window, which contains records for
  1892.  *  all other windows as children.
  1893.  * ------------------------------------------------------------------------
  1894.  */
  1895. static DD_WinRep*
  1896. GetWinRepInfo(dsPtr,ddlist)
  1897.     DD_Source *dsPtr;     /* drag&drop source window */
  1898.     DD_RegList *ddlist;   /* drag&drop registration info */
  1899. {
  1900.     DD_WinRep *wr;
  1901.  
  1902.     wr = WinRepAlloc(ddlist);
  1903.     wr->win = DefaultRootWindow(dsPtr->display);
  1904.     WinRepInit(wr, dsPtr);
  1905.  
  1906.     return wr;
  1907. }
  1908.  
  1909. /*
  1910.  * ------------------------------------------------------------------------
  1911.  *  FindTargetWin()
  1912.  *
  1913.  *  Checks to see if a compatible drag&drop target exists at the given
  1914.  *  position.  A target is "compatible" if it is a drag&drop window,
  1915.  *  and if it has a handler that is compatible with the current source
  1916.  *  window.
  1917.  *
  1918.  *  Returns a pointer to a structure describing the target, or NULL
  1919.  *  if no compatible target is found.
  1920.  * ------------------------------------------------------------------------
  1921.  */
  1922. static DD_WinRep*
  1923. FindTargetWin(dsPtr,x,y)
  1924.     DD_Source *dsPtr;     /* drag&drop source window */
  1925.     int x,y;              /* current drag&drop location (virtual coords) */
  1926. {
  1927.     int vx, vy;
  1928.     unsigned int vw, vh;
  1929.     register char *type;
  1930.  
  1931.     DD_WinRep *wr, *wrkid;
  1932.     DD_Stack stack;
  1933.     DD_SourceHndl *shandl;
  1934.  
  1935.     /*
  1936.      *  If window representations have not yet been built, then
  1937.      *  abort this call.  This probably means that the token is being
  1938.      *  moved before it has been properly built.
  1939.      */
  1940.     if (!dsPtr->allwins)
  1941.         return NULL;
  1942.  
  1943.     /*
  1944.      *  Adjust current location for virtual root windows.
  1945.      */
  1946.     Tk_GetVRootGeometry(dsPtr->tkwin, &vx, &vy, &vw, &vh);
  1947.     x += vx;
  1948.     y += vy;
  1949.  
  1950.     /*
  1951.      *  Build a stack of all windows containing the given point,
  1952.      *  in order from least to most specific.
  1953.      */
  1954.     StackInit(&stack);
  1955.  
  1956.     wr = dsPtr->allwins;
  1957.     if ((x >= wr->x0) && (x <= wr->x1) &&
  1958.         (y >= wr->y0) && (y <= wr->y1))
  1959.         StackPush((ClientData)wr, &stack);
  1960.  
  1961.     while (wr)
  1962.     {
  1963.         for (wrkid=wr->kids; wrkid; wrkid=wrkid->next)
  1964.         {
  1965.             if (!wrkid->initialized)
  1966.                 WinRepInit(wrkid, dsPtr);
  1967.  
  1968.             if ((x >= wrkid->x0) && (x <= wrkid->x1) &&
  1969.                 (y >= wrkid->y0) && (y <= wrkid->y1))
  1970.             {
  1971.                 StackPush((ClientData)wrkid, &stack);
  1972.                 break;
  1973.             }
  1974.         }
  1975.         wr = wrkid;  /* continue search */
  1976.     }
  1977.  
  1978.     /*
  1979.      *  Pop windows from the stack until one containing a
  1980.      *  "DragDropInfo" property is found.  See if the handlers
  1981.      *  listed in this property are compatible with the
  1982.      *  given source.
  1983.      */
  1984.     while ((wr=(DD_WinRep*)StackPop(&stack)) != NULL)
  1985.         if (wr->ddprop)
  1986.             break;
  1987.  
  1988.     if (wr && wr->ddhandlers)
  1989.     {
  1990.         type = wr->ddhandlers;
  1991.         while (*type != '\0')
  1992.         {
  1993.             for (shandl=dsPtr->handlers; shandl; shandl=shandl->next)
  1994.                 if (strcmp(shandl->dataType, type) == 0)
  1995.                     break;
  1996.  
  1997.             if (shandl)   /* found a match? */
  1998.                 break;    /* then stop searching */
  1999.             else          /* otherwise, move to next handler type */
  2000.             {
  2001.                 while (*type++ != '\0')
  2002.                     ;
  2003.             }
  2004.         }
  2005.         if (*type == '\0')  /* no handler match? */
  2006.             wr = NULL;      /* then return NULL */
  2007.     }
  2008.     StackDelete(&stack);
  2009.  
  2010.     return wr;
  2011. }
  2012.  
  2013.  
  2014. /*
  2015.  * ------------------------------------------------------------------------
  2016.  *  WinRepAlloc()
  2017.  *
  2018.  *  Returns a new structure for representing X window position info.
  2019.  *  Such structures are typically allocated at the start of a drag&drop
  2020.  *  operation to capture the placement of all windows on the root
  2021.  *  window.  The drag&drop registration list keeps a pool of such
  2022.  *  structures so that they can be allocated quickly when needed.
  2023.  *  Returns a pointer to an empty structure.
  2024.  * ------------------------------------------------------------------------
  2025.  */
  2026. static DD_WinRep*
  2027. WinRepAlloc(ddlist)
  2028.     DD_RegList *ddlist;  /* drag&drop registration list */
  2029. {
  2030.     DD_WinRep *wr;
  2031.  
  2032.     /*
  2033.      *  Return the top-most structure in the pool.
  2034.      *  If the pool is empty, add a new structure to it.
  2035.      */
  2036.     if (!ddlist->pool)
  2037.     {
  2038.         wr = (DD_WinRep*)malloc(sizeof(DD_WinRep));
  2039.         wr->next = NULL;
  2040.         ddlist->pool = wr;
  2041.     }
  2042.     wr = ddlist->pool;
  2043.     ddlist->pool = wr->next;
  2044.  
  2045.     wr->initialized = 0;
  2046.     wr->ddprop = NULL;
  2047.     wr->ddinterp = wr->ddwin = wr->ddhandlers = NULL;
  2048.     wr->parent = wr->kids = wr->next = NULL;
  2049.     return wr;
  2050. }
  2051.  
  2052. /*
  2053.  * ------------------------------------------------------------------------
  2054.  *  WinRepRelease()
  2055.  *
  2056.  *  Puts a window representation structure back into the global pool,
  2057.  *  making it available for future calls to WinRepAlloc().  Any
  2058.  *  associated resources (within the structure) are automatically freed.
  2059.  * ------------------------------------------------------------------------
  2060.  */
  2061. static void
  2062. WinRepRelease(wr,ddlist)
  2063.     DD_WinRep *wr;       /* window rep to be freed */
  2064.     DD_RegList *ddlist;  /* drag&drop registration list */
  2065. {
  2066.     DD_WinRep *wrkid, *wrnext;
  2067.  
  2068.     for (wrkid=wr->kids; wrkid; wrkid=wrnext)
  2069.     {
  2070.         wrnext = wrkid->next;
  2071.         WinRepRelease(wrkid,ddlist);
  2072.     }
  2073.  
  2074.     if (wr->ddprop)
  2075.         XFree(wr->ddprop);
  2076.  
  2077.     wr->next = ddlist->pool;  /* put back into pool */
  2078.     ddlist->pool = wr;
  2079. }
  2080.  
  2081.  
  2082. /*
  2083.  * ------------------------------------------------------------------------
  2084.  *  WinRepInit()
  2085.  *
  2086.  *  Invoked during "drag" operations to dig a little deeper into the
  2087.  *  root window hierarchy and cache the resulting information.  If a
  2088.  *  point coordinate lies within an uninitialized DD_WinRep, this
  2089.  *  routine is called to query window coordinates and drag&drop info.
  2090.  *  If this particular window has any children, more uninitialized
  2091.  *  DD_WinRep structures are allocated.  Further queries will cause
  2092.  *  these structures to be initialized in turn.
  2093.  * ------------------------------------------------------------------------
  2094.  */
  2095. static void
  2096. WinRepInit(wr,dsPtr)
  2097.     DD_WinRep *wr;         /* window rep to be initialized */
  2098.     DD_Source *dsPtr;      /* drag&drop source managing win rep */
  2099. {
  2100.     Window ignoreSource = Tk_WindowId(dsPtr->tkwin);
  2101.     Window ignoreToken  = Tk_WindowId(dsPtr->tokenwin);
  2102.  
  2103.     DD_WinRep *wrkid, *wrtail;
  2104.  
  2105.     Window root, parent, *kids;
  2106.     unsigned int nkids;
  2107.     XWindowAttributes winInfo;
  2108.  
  2109.     char *propInfo;
  2110.     int i, result, actualFormat;
  2111.     Atom actualType;
  2112.     unsigned long numItems, bytesAfter;
  2113.  
  2114.     /*
  2115.      *  If the self-target flag is set, allow the source window to
  2116.      *  drop onto itself.  Do not ignore source window during search.
  2117.      */
  2118.     if (dsPtr->selfTarget)
  2119.         ignoreSource = None;
  2120.  
  2121.     if (!wr->initialized)
  2122.     {
  2123.         /*
  2124.          *  Query for the window coordinates.
  2125.          */
  2126.         if (XGetWindowAttributes(dsPtr->display, wr->win, &winInfo) &&
  2127.             (winInfo.map_state == IsViewable) &&
  2128.             (wr->win != ignoreToken) &&
  2129.             (wr->win != ignoreSource))
  2130.         {
  2131.             wr->x0  = winInfo.x;
  2132.             wr->y0  = winInfo.y;
  2133.             wr->x1  = winInfo.x + winInfo.width;
  2134.             wr->y1  = winInfo.y + winInfo.height;
  2135.  
  2136.             if (wr->parent)  /* offset by parent coords */
  2137.             {
  2138.                 wr->x0 += wr->parent->x0;
  2139.                 wr->y0 += wr->parent->y0;
  2140.                 wr->x1 += wr->parent->x0;
  2141.                 wr->y1 += wr->parent->y0;
  2142.             }
  2143.         }
  2144.         else
  2145.         {
  2146.             wr->x0 = wr->y0 = -1;
  2147.             wr->x1 = wr->y1 = -1;
  2148.         }
  2149.  
  2150.         /*
  2151.          *  See if this window has a "DragDropInfo" property.
  2152.          */
  2153.         result = XGetWindowProperty(dsPtr->display, wr->win,
  2154.             dsPtr->ddAtom, 0, MAX_PROP_SIZE, False, XA_STRING,
  2155.             &actualType, &actualFormat,
  2156.             &numItems, &bytesAfter, (unsigned char**)&propInfo);
  2157.  
  2158.         if ((result != Success) ||
  2159.             (actualFormat != 8) ||
  2160.             (actualType != XA_STRING))
  2161.         {
  2162.             if (propInfo != NULL) {
  2163.                 XFree((caddr_t)propInfo);
  2164.             }
  2165.             propInfo = NULL;
  2166.         }
  2167.  
  2168.         wr->ddprop = propInfo;
  2169.         if (wr->ddprop)
  2170.         {
  2171.             char *p = wr->ddprop;
  2172.             wr->ddinterp = wr->ddprop;
  2173.  
  2174. #ifdef ORIGINAL_CODE
  2175.             while ((*p != '\0') && (*p != ']'))
  2176. #else
  2177.             while ((*p != '\0') && (*p != '}'))
  2178. #endif
  2179.                 p++;
  2180.  
  2181.             if (*p != '\0')
  2182.             {
  2183.                 *p++ = '\0';         /* terminate interp name */
  2184.                 wr->ddwin = p;       /* get start of window name */
  2185.             }
  2186.  
  2187. #ifdef ORIGINAL_CODE
  2188.             while ((*p != '\0') && (*p != ']'))
  2189. #else
  2190.             while ((*p != '\0') && (*p != '}'))
  2191. #endif
  2192.                 p++;
  2193.  
  2194.             if (*p != '\0')
  2195.             {
  2196.                 *p++ = '\0';         /* terminate window name */
  2197.                 wr->ddhandlers = p;  /* get start of handler list */
  2198.  
  2199.                 /*
  2200.                  *  Handler strings are of the form:
  2201.                  *  "<type> <type> ... <type> "
  2202.                  */
  2203.                 while (*p != '\0')
  2204.                 {
  2205.                     while ((*p != ' ') && (*p != '\0'))
  2206.                         p++;
  2207.  
  2208.                     *p++ = '\0';  /* null terminate handler type */
  2209.                 }
  2210.             }
  2211.         }
  2212.  
  2213.         /*
  2214.          *  If this window has any children, then create DD_WinReps
  2215.          *  for them as well.
  2216.          */
  2217.         if (XQueryTree(dsPtr->display, wr->win, &root, &parent, &kids, &nkids))
  2218.         {
  2219.             wrtail = NULL;
  2220.             for (i=nkids-1; i >= 0; i--)
  2221.             {
  2222.                 wrkid = WinRepAlloc(dsPtr->ddlist);
  2223.                 wrkid->win = kids[i];
  2224.                 wrkid->parent = wr;
  2225.  
  2226.                 if (wrtail)
  2227.                     wrtail->next = wrkid;
  2228.                 else
  2229.                     wr->kids = wrkid;
  2230.  
  2231.                 wrtail = wrkid;
  2232.             }
  2233.             if (kids != NULL) {
  2234.                 XFree((caddr_t)kids);   /* done with list of kids */
  2235.             }
  2236.         }
  2237.     }
  2238.     wr->initialized = ~0;
  2239. }
  2240.  
  2241.  
  2242. /*
  2243.  * ------------------------------------------------------------------------
  2244.  *  AddDDProp()
  2245.  *
  2246.  *  Attaches a "DragDropInfo" property to the given target window.  This
  2247.  *  property allows the drag&drop mechanism to recognize the window as
  2248.  *  a valid target, and stores important information including the
  2249.  *  interpreter managing the target and the pathname for the target
  2250.  *  window.  Usually invoked when the target is first registered or
  2251.  *  first exposed (so that the X-window really exists).
  2252.  * ------------------------------------------------------------------------
  2253.  */
  2254. static void
  2255. AddDDProp(dtPtr)
  2256.     DD_Target* dtPtr;  /* drag&drop target window data */
  2257. {
  2258.     Tcl_Interp *interp = dtPtr->ddlist->interp;
  2259.  
  2260.     Atom ddProperty;
  2261.     char buffer[MAX_PROP_SIZE], *path, *info;
  2262.     DD_TargetHndl *thandl;
  2263.  
  2264.     if (dtPtr->tkwin != None)
  2265.     {
  2266. #ifdef ORIGINAL_CODE
  2267.         static char command[] = { "winfo name ." };
  2268. #else
  2269.         static char command[] = { "(winfo 'name *root*)" };
  2270. #endif
  2271.  
  2272.         path = Tk_PathName(dtPtr->tkwin);
  2273. #ifdef ORIGINAL_CODE
  2274.         if (Tcl_Eval(interp, command)==TCL_OK)
  2275.             sprintf(buffer, "%s]%s]", interp->result, path);
  2276.         else
  2277.             sprintf(buffer, "]%s]", path);
  2278. #else
  2279.         if (Tcl_Eval(interp, command)==TCL_OK)
  2280.             sprintf(buffer, "%s}%s}", interp->result, path);
  2281.         else
  2282.             sprintf(buffer, "}%s}", path);
  2283. #endif
  2284.  
  2285.         Tcl_SetResult(interp, buffer, TCL_VOLATILE);
  2286.         for (thandl=dtPtr->handlers; thandl; thandl=thandl->next)
  2287.             Tcl_AppendResult(interp, thandl->dataType, " ", (char*)NULL);
  2288.  
  2289.         ddProperty = XInternAtom(dtPtr->display, DRAGDROP_PROPINFO, False);
  2290.         info = interp->result;
  2291.  
  2292.         XChangeProperty(dtPtr->display, Tk_WindowId(dtPtr->tkwin),
  2293.             ddProperty, XA_STRING, 8, PropModeReplace,
  2294.             (unsigned char*)info, strlen(info)+1);
  2295.     }
  2296. }
  2297.  
  2298. /*
  2299.  * ------------------------------------------------------------------------
  2300.  *  DDTokenEventProc()
  2301.  *
  2302.  *  Invoked by the Tk dispatcher to handle widget events.
  2303.  *  Manages redraws for the drag&drop token window.
  2304.  * ------------------------------------------------------------------------
  2305.  */
  2306. static void
  2307. DDTokenEventProc(clientData, eventPtr)
  2308.     ClientData clientData;     /* data associated with widget */
  2309.     XEvent *eventPtr;          /* information about event */
  2310. {
  2311.     register DD_Source *dsPtr = (DD_Source*)clientData;
  2312.  
  2313.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0))
  2314.     {
  2315.         if (dsPtr->tokenwin)
  2316.         {
  2317.             Tk_Window tkwin = dsPtr->tokenwin;
  2318.             int bd;
  2319.  
  2320.             bd = dsPtr->tokenBorderWidth;
  2321.             Tk_Fill3DRectangle(dsPtr->display, Tk_WindowId(tkwin),
  2322.                 dsPtr->tokenOutline,
  2323.                 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  2324.                 0, TK_RELIEF_FLAT);
  2325.             Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
  2326.                 dsPtr->tokenBorder,
  2327.                 bd, bd, Tk_Width(tkwin)-2*bd, Tk_Height(tkwin)-2*bd,
  2328.                 bd, (dsPtr->overTargetWin) ? TK_RELIEF_RAISED : TK_RELIEF_FLAT);
  2329.         }
  2330.     }
  2331.     else if (eventPtr->type == DestroyNotify)
  2332.         dsPtr->tokenwin = NULL;
  2333. }
  2334.  
  2335. /*
  2336.  * ------------------------------------------------------------------------
  2337.  *  MoveDDToken()
  2338.  *
  2339.  *  Invoked during "drag" operations to move a token window to its
  2340.  *  current "drag" coordinate.
  2341.  * ------------------------------------------------------------------------
  2342.  */
  2343. static void
  2344. MoveDDToken(dsPtr)
  2345.     DD_Source *dsPtr;  /* drag&drop source window data */
  2346. {
  2347.     int x = dsPtr->tokenx;
  2348.     int y = dsPtr->tokeny;
  2349.     Tk_Window tokenwin = dsPtr->tokenwin;
  2350.  
  2351.     int max;
  2352.     int vx, vy;
  2353.     unsigned int vw, vh;
  2354.  
  2355.     /*
  2356.      *  Adjust current location for virtual root windows.
  2357.      */
  2358.     Tk_GetVRootGeometry(dsPtr->tkwin, &vx, &vy, &vw, &vh);
  2359.     x += vx;
  2360.     y += vy;
  2361.  
  2362.     max = WidthOfScreen(Tk_Screen(dsPtr->tkwin)) - Tk_Width(tokenwin);
  2363.     switch (dsPtr->tokenAnchor)
  2364.     {
  2365.     case TK_ANCHOR_NW:
  2366.     case TK_ANCHOR_W:
  2367.     case TK_ANCHOR_SW:
  2368.         break;
  2369.  
  2370.     case TK_ANCHOR_N:
  2371.     case TK_ANCHOR_CENTER:
  2372.     case TK_ANCHOR_S:
  2373.         x -= Tk_Width(tokenwin)/2;
  2374.         break;
  2375.  
  2376.     case TK_ANCHOR_NE:
  2377.     case TK_ANCHOR_E:
  2378.     case TK_ANCHOR_SE:
  2379.         x -= Tk_Width(tokenwin);
  2380.         break;
  2381.     }
  2382.     if (x > max) {
  2383.         x = max;
  2384.     } else if (x < 0) {
  2385.         x = 0;
  2386.     }
  2387.     max = HeightOfScreen(Tk_Screen(dsPtr->tkwin)) - Tk_Height(tokenwin);
  2388.     switch (dsPtr->tokenAnchor)
  2389.     {
  2390.     case TK_ANCHOR_NW:
  2391.     case TK_ANCHOR_N:
  2392.     case TK_ANCHOR_NE:
  2393.         break;
  2394.  
  2395.     case TK_ANCHOR_W:
  2396.     case TK_ANCHOR_CENTER:
  2397.     case TK_ANCHOR_E:
  2398.         y -= Tk_Height(tokenwin)/2;
  2399.         break;
  2400.  
  2401.     case TK_ANCHOR_SW:
  2402.     case TK_ANCHOR_S:
  2403.     case TK_ANCHOR_SE:
  2404.         y -= Tk_Height(tokenwin);
  2405.         break;
  2406.     }
  2407.     if (y > max) {
  2408.         y = max;
  2409.     } else if (y < 0) {
  2410.         y = 0;
  2411.     }
  2412.     if ((x != Tk_X(tokenwin)) || (y != Tk_Y(tokenwin)))
  2413.         Tk_MoveWindow(tokenwin, x, y);
  2414. }
  2415.  
  2416. /*
  2417.  * ------------------------------------------------------------------------
  2418.  *  UpdateDDToken()
  2419.  *
  2420.  *  Invoked when the event loop is idle to determine whether or not
  2421.  *  the current drag&drop token position is over another drag&drop
  2422.  *  target.
  2423.  * ------------------------------------------------------------------------
  2424.  */
  2425. static void
  2426. UpdateDDToken(clientData)
  2427.     ClientData clientData;  /* widget data */
  2428. {
  2429.     register DD_Source *dsPtr = (DD_Source*)clientData;
  2430.     Tk_Window tkwin = dsPtr->tokenwin;
  2431.  
  2432.     int status, bd;
  2433.  
  2434.     status = (FindTargetWin(dsPtr, dsPtr->tokenx, dsPtr->tokeny) != NULL);
  2435.  
  2436.     bd = dsPtr->tokenBorderWidth;
  2437.     if (dsPtr->overTargetWin ^ status)
  2438.     {
  2439.         Tk_Fill3DRectangle(dsPtr->display, Tk_WindowId(tkwin),
  2440.             dsPtr->tokenOutline,
  2441.             0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  2442.             0, TK_RELIEF_FLAT);
  2443.         Tk_Fill3DRectangle(dsPtr->display, Tk_WindowId(tkwin),
  2444.             dsPtr->tokenBorder,
  2445.             bd, bd, Tk_Width(tkwin)-2*bd, Tk_Height(tkwin)-2*bd,
  2446.             bd, (status) ? TK_RELIEF_RAISED : TK_RELIEF_FLAT);
  2447.  
  2448.         /*
  2449.          *  If the source has a site command, then invoke it to
  2450.          *  modify the appearance of the token window.  Pass any
  2451.          *  errors onto the drag&drop error handler.
  2452.          */
  2453.         if (dsPtr->sitecmd)
  2454.         {
  2455.             char buffer[200];
  2456.  
  2457.             sprintf(buffer, "%d", status);
  2458.             if ((Tcl_VarEval(dsPtr->ddlist->interp,
  2459. #ifdef ORIGINAL_CODE
  2460.                     dsPtr->sitecmd," ",buffer," ",Tk_PathName(dsPtr->tokenwin),
  2461. #else
  2462.                     "(", dsPtr->sitecmd," ",buffer," '",Tk_PathName(dsPtr->tokenwin), ")",
  2463. #endif
  2464.                     (char*)NULL) != TCL_OK) &&
  2465.                 dsPtr->ddlist->errorProc && *dsPtr->ddlist->errorProc)
  2466.  
  2467.                 (void) Tcl_VarEval(dsPtr->ddlist->interp,
  2468. #ifdef ORIGINAL_CODE
  2469.                     dsPtr->ddlist->errorProc, " {",
  2470.                     dsPtr->ddlist->interp->result, "}",
  2471. #else
  2472.                     "(", dsPtr->ddlist->errorProc, " ",
  2473.                      (char *) STk_Stringify(dsPtr->ddlist->interp->result, 0), ")",
  2474. #endif
  2475.                     (char*)NULL);
  2476.         }
  2477.     }
  2478.     dsPtr->overTargetWin = status;
  2479. }
  2480.  
  2481. /*
  2482.  * ------------------------------------------------------------------------
  2483.  *  HideDDToken()
  2484.  *
  2485.  *  Unmaps the drag&drop token.  Invoked directly at the end of a
  2486.  *  successful communication, or after a delay if the communication
  2487.  *  fails (allowing the user to see a graphical picture of failure).
  2488.  * ------------------------------------------------------------------------
  2489.  */
  2490. static void
  2491. HideDDToken(clientData)
  2492.     ClientData clientData;  /* widget data */
  2493. {
  2494.     register DD_Source *dsPtr = (DD_Source*)clientData;
  2495.  
  2496.     if (dsPtr->tokenwin)
  2497.         Tk_UnmapWindow(dsPtr->tokenwin);
  2498.  
  2499.     dsPtr->hidetoken = NULL;
  2500. }
  2501.  
  2502. /*
  2503.  * ------------------------------------------------------------------------
  2504.  *  RejectDDToken()
  2505.  *
  2506.  *  Draws a rejection mark on the current drag&drop token, and arranges
  2507.  *  for the token to be unmapped after a small delay.
  2508.  * ------------------------------------------------------------------------
  2509.  */
  2510. static void
  2511. RejectDDToken(dsPtr)
  2512.     DD_Source* dsPtr;  /* widget data */
  2513. {
  2514.     int div = 6;      /* controls size of rejection symbol */
  2515.     int w,h,lwid,x,y,margin;
  2516.  
  2517.     Tk_Window tkwin = dsPtr->tokenwin;
  2518.  
  2519.     margin = 2*dsPtr->tokenBorderWidth;
  2520.     w = Tk_Width(tkwin) - 2*margin;
  2521.     h = Tk_Height(tkwin) - 2*margin;
  2522.  
  2523.     lwid = (w < h) ? w/div : h/div;
  2524.     lwid = (lwid < 1) ? 1 : lwid;
  2525.  
  2526.     w = h = lwid*(div-1);
  2527.     x = (Tk_Width(tkwin) - w)/2;
  2528.     y = (Tk_Height(tkwin) - h)/2;
  2529.  
  2530.     /*
  2531.      *  Draw the rejection symbol background (\) on the token window...
  2532.      */
  2533.     XSetLineAttributes(Tk_Display(tkwin), dsPtr->rejectBgGC,
  2534.         lwid+4, LineSolid, CapButt, JoinBevel);
  2535.  
  2536.     XDrawArc(Tk_Display(tkwin), Tk_WindowId(tkwin), dsPtr->rejectBgGC,
  2537.         x, y, w, h, 0, 23040);
  2538.  
  2539.     XDrawLine(Tk_Display(tkwin), Tk_WindowId(tkwin), dsPtr->rejectBgGC,
  2540.         x+lwid, y+lwid, x+w-lwid, y+h-lwid);
  2541.  
  2542.     /*
  2543.      *  Draw the rejection symbol foreground (\) on the token window...
  2544.      */
  2545.     XSetLineAttributes(Tk_Display(tkwin), dsPtr->rejectFgGC,
  2546.         lwid, LineSolid, CapButt, JoinBevel);
  2547.  
  2548.     XDrawArc(Tk_Display(tkwin), Tk_WindowId(tkwin), dsPtr->rejectFgGC,
  2549.         x, y, w, h, 0, 23040);
  2550.  
  2551.     XDrawLine(Tk_Display(tkwin), Tk_WindowId(tkwin), dsPtr->rejectFgGC,
  2552.         x+lwid, y+lwid, x+w-lwid, y+h-lwid);
  2553.  
  2554.     /*
  2555.      *  Arrange for token window to disappear eventually.
  2556.      */
  2557.     dsPtr->hidetoken
  2558.         = Tk_CreateTimerHandler(1000, HideDDToken, (ClientData)dsPtr);
  2559. }
  2560.  
  2561.  
  2562. /*
  2563.  * ------------------------------------------------------------------------
  2564.  *  StackInit()
  2565.  *
  2566.  *  Initializes a stack structure, allocating a certain amount of memory
  2567.  *  for the stack and setting the stack length to zero.
  2568.  * ------------------------------------------------------------------------
  2569.  */
  2570. static void
  2571. StackInit(stack)
  2572.     DD_Stack *stack;     /* stack to be initialized */
  2573. {
  2574.     stack->values = stack->space;
  2575.     stack->max = sizeof(stack->space)/sizeof(ClientData);
  2576.     stack->len = 0;
  2577. }
  2578.  
  2579. /*
  2580.  * ------------------------------------------------------------------------
  2581.  *  StackDelete()
  2582.  *
  2583.  *  Destroys a stack structure, freeing any memory that may have been
  2584.  *  allocated to represent it.
  2585.  * ------------------------------------------------------------------------
  2586.  */
  2587. static void
  2588. StackDelete(stack)
  2589.     DD_Stack *stack;     /* stack to be deleted */
  2590. {
  2591.     if (stack->values != stack->space)  /* allocated extra memory? */
  2592.         free((char*)stack->values);   /* then free it */
  2593.  
  2594.     stack->values = NULL;
  2595.     stack->len = stack->max = 0;
  2596. }
  2597.  
  2598. /*
  2599.  * ------------------------------------------------------------------------
  2600.  *  StackPush()
  2601.  *
  2602.  *  Pushes a piece of client data onto the top of the given stack.
  2603.  * ------------------------------------------------------------------------
  2604.  */
  2605. static void
  2606. StackPush(cdata,stack)
  2607.     ClientData cdata;    /* data to be pushed onto stack */
  2608.     DD_Stack *stack;     /* stack */
  2609. {
  2610.     ClientData *newStack;
  2611.  
  2612.     if (stack->len+1 >= stack->max)
  2613.     {
  2614.         stack->max = (stack->max == 0) ? 5 : 2*stack->max;
  2615.         newStack = (ClientData*)
  2616.             malloc((unsigned)(stack->max*sizeof(ClientData)));
  2617.  
  2618.         if (stack->values)
  2619.         {
  2620.             memcpy((char *)newStack, (char *)stack->values,
  2621.                 stack->len*sizeof(ClientData));
  2622.  
  2623.             if (stack->values != stack->space)
  2624.                 free((char*)stack->values);
  2625.         }
  2626.         stack->values = newStack;
  2627.     }
  2628.     stack->values[stack->len++] = cdata;
  2629. }
  2630.  
  2631. /*
  2632.  * ------------------------------------------------------------------------
  2633.  *  StackPop()
  2634.  *
  2635.  *  Pops a bit of client data from the top of the given stack.
  2636.  * ------------------------------------------------------------------------
  2637.  */
  2638. static ClientData
  2639. StackPop(stack)
  2640.     DD_Stack *stack;  /* stack to be manipulated */
  2641. {
  2642.     if ((stack->values != NULL) && (stack->len > 0)) {
  2643.         return (stack->values[--stack->len]);
  2644.     }
  2645.     return (ClientData) 0;
  2646. }
  2647.